Compare commits

...

19 Commits

Author SHA1 Message Date
vchikalkin
0fccfc4932 fix radioDeliveryTime status reaction 2023-04-06 17:49:41 +03:00
vchikalkin
d92f7ccebb used-pl/validation: add empty tbxVIN check 2023-04-06 17:40:49 +03:00
vchikalkin
7394350211 fix:
Добавить реакцию на изменение поля Пересчет без пересмотра cbxRecalcWithRevision и загрузку КП:
   * ЕСЛИ в поле Продукт selectProduct  выбрана запись и поле БУбезНДС evo_sale_without_nds = True
   * и Пересчет без пересмотра cbxRecalcWithRevision = True,
   *
   * то поля "Первый платеж, руб." tbxFirstPaymentRub
   * и "Первый платеж %" tbxFirstPaymentPerc закрыты для редактирования,
   * иначе открыты для редактирования
2023-04-06 17:34:17 +03:00
vchikalkin
445d527c5a fix tbxLastPaymentRub & tbxLastPaymentPerc 2023-04-06 17:23:58 +03:00
vchikalkin
09c9028a93 payments: сбрасываем tbxParmentsDecreasePercent при изменении radioGraphType 2023-04-06 16:45:30 +03:00
vchikalkin
dd049f8876 payments: генерируем график дегрессии во время загрузки КП 2023-04-06 16:43:03 +03:00
vchikalkin
058419496d fix: disable resetValue selectSeasonType selectHighSeasonStart on load-kp 2023-04-06 15:51:48 +03:00
vchikalkin
6c08b3e909 store/values: remove null check in setValues 2023-04-06 13:56:49 +03:00
vchikalkin
d037580d1f get-kp-data: fix get model 2023-04-06 13:26:34 +03:00
vchikalkin
401d92d0ff InputNumber: use default formatter for user typing 2023-04-06 11:14:40 +03:00
vchikalkin
2a3cc80dcc fix load-kp caches values (WTF?) 2023-04-06 11:06:28 +03:00
vchikalkin
e6d7b17376 round priceWithVAT to 2 2023-04-05 18:34:36 +03:00
vchikalkin
591abe97c5 fix formatting numbers 2023-04-05 18:31:46 +03:00
vchikalkin
a747af7333 comission: дополнили формулы 2023-04-05 15:06:06 +03:00
vchikalkin
9a257fd987 selectEngineType: set null when evo_leasingobject_type?.evo_id === '8' 2023-04-05 14:46:52 +03:00
vchikalkin
f42ab9bcb9 labelDepreciationGroup: убрали зависимость от комплектации 2023-04-05 14:39:36 +03:00
vchikalkin
22a87a35ca починили фильтрацию selectRegistration
добавили сброс tbxVehicleTaxInYear & radioTypePTS
2023-04-05 14:29:44 +03:00
vchikalkin
41082f12fe На изменение Тип предмета лизинга leaseObjectType, Категория ТС leaseObjectCategory, Тип ПТС typePTS или На кого регистрация ТС objectRegistration
Если objectRegistration = Лизингодатель (100000001) И Тип ПТС typePTS = Электронный (100000001) ,

то Категория в соответствии с ТР ТС 018/2011 objectCategoryTax открыто для редактирования

И в поле Категория в соответствии с ТР ТС 018/2011 objectCategoryTax осуществляется фильтр списка - в списке должны отражаться только те значения, которые содержатся в мультипиклисте evo_category_tr в Типе предмета лизинга, указанного в поле leaseObjectType, при условии что значение поля Категория ТС leaseObjectCategory = evo_category в Типе предмета лизинга.

иначе Категория в соответствии с ТР ТС 018/2011 objectCategoryTax = null, список для выбора пустой и закрыто для редактирования.
2023-04-05 12:44:40 +03:00
vchikalkin
75d60246ac process/load-kp: fix create-kp triggered load-kp 2023-04-04 15:11:02 +03:00
18 changed files with 375 additions and 275 deletions

View File

@ -3,8 +3,8 @@ import { buildOptionComponent, buildValueComponent } from './builders';
import type * as Insurance from './types'; import type * as Insurance from './types';
import { MAX_INSURANCE } from '@/constants/values'; import { MAX_INSURANCE } from '@/constants/values';
import type { ColumnsType } from 'antd/lib/table'; import type { ColumnsType } from 'antd/lib/table';
import { formatter, parser } from 'tools/number'; import { parser } from 'tools/number';
import InputNumber from 'ui/elements/InputNumber'; import InputNumber, { createFormatter } from 'ui/elements/InputNumber';
import Select from 'ui/elements/Select'; import Select from 'ui/elements/Select';
export const columns: ColumnsType<Insurance.RowValues> = [ export const columns: ColumnsType<Insurance.RowValues> = [
@ -44,7 +44,7 @@ export const columns: ColumnsType<Insurance.RowValues> = [
return ( return (
<Component <Component
addonAfter="₽" addonAfter="₽"
formatter={formatter} formatter={createFormatter({ minimumFractionDigits: 2, maximumFractionDigits: 2 })}
max={MAX_INSURANCE} max={MAX_INSURANCE}
min={0} min={0}
parser={parser} parser={parser}

View File

@ -4,8 +4,11 @@ import CurrencyAddon from '../addons/currency-addon';
import type { ElementsProps } from './elements-components'; import type { ElementsProps } from './elements-components';
import { MAX_FRANCHISE, MAX_LEASING_PERIOD } from '@/constants/values'; import { MAX_FRANCHISE, MAX_LEASING_PERIOD } from '@/constants/values';
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import { formatter, formatterExtra, parser } from 'tools/number'; import { parser } from 'tools/number';
import { DownloadOutlined, PlusOutlined } from 'ui/elements/icons'; import { DownloadOutlined, PlusOutlined } from 'ui/elements/icons';
import { createFormatter } from 'ui/elements/InputNumber';
const formatter = createFormatter({ minimumFractionDigits: 2, maximumFractionDigits: 2 });
const props: Partial<ElementsProps> = { const props: Partial<ElementsProps> = {
tbxLeaseObjectPrice: { tbxLeaseObjectPrice: {
@ -77,7 +80,7 @@ const props: Partial<ElementsProps> = {
max: 50, max: 50,
precision: 4, precision: 4,
parser, parser,
formatter: formatterExtra, formatter: createFormatter({ minimumFractionDigits: 4, maximumFractionDigits: 4 }),
addonAfter: '%', addonAfter: '%',
}, },
tbxFirstPaymentRub: { tbxFirstPaymentRub: {
@ -95,7 +98,7 @@ const props: Partial<ElementsProps> = {
step: 1, step: 1,
precision: 6, precision: 6,
parser, parser,
formatter: formatterExtra, formatter: createFormatter({ minimumFractionDigits: 6, maximumFractionDigits: 6 }),
addonAfter: '%', addonAfter: '%',
}, },
tbxLastPaymentRub: { tbxLastPaymentRub: {
@ -203,7 +206,7 @@ const props: Partial<ElementsProps> = {
step: 0.5, step: 0.5,
precision: 4, precision: 4,
parser, parser,
formatter: formatterExtra, formatter: createFormatter({ minimumFractionDigits: 4, maximumFractionDigits: 4 }),
addonAfter: 'л', addonAfter: 'л',
}, },
tbxMaxMass: { tbxMaxMass: {
@ -363,7 +366,7 @@ const props: Partial<ElementsProps> = {
step: 0.0001, step: 0.0001,
precision: 6, precision: 6,
parser, parser,
formatter: formatterExtra, formatter: createFormatter({ minimumFractionDigits: 6, maximumFractionDigits: 6 }),
addonAfter: '%', addonAfter: '%',
}, },
linkDownloadKp: { linkDownloadKp: {

View File

@ -1,5 +1,4 @@
import type { ResultValues } from '@/stores/results/types'; import type { ResultValues } from '@/stores/results/types';
import { moneyFormatter, percentFormatter } from 'tools';
export const id = 'output'; export const id = 'output';
export const title = 'Результаты'; export const title = 'Результаты';
@ -26,6 +25,17 @@ export const titles: Record<keyof ResultValues, string> = {
resultTotalGraphwithNDS: 'Итого по графику, с НДС', resultTotalGraphwithNDS: 'Итого по графику, с НДС',
}; };
const moneyFormatter = Intl.NumberFormat('ru', {
currency: 'RUB',
style: 'currency',
}).format;
const percentFormatter = Intl.NumberFormat('ru', {
maximumFractionDigits: 2,
minimumFractionDigits: 2,
style: 'percent',
}).format;
export const formatters = { export const formatters = {
resultAB_FL: moneyFormatter, resultAB_FL: moneyFormatter,
resultAB_UL: moneyFormatter, resultAB_UL: moneyFormatter,

View File

@ -1,7 +1,10 @@
import helper from '../lib/helper'; import helper from '../lib/helper';
import type { ProcessContext } from '@/process/types'; import type { ProcessContext } from '@/process/types';
import { reaction } from 'mobx'; import { reaction } from 'mobx';
import { formatter } from 'tools';
export const formatter = Intl.NumberFormat('ru', {
minimumFractionDigits: 2,
}).format;
export default function reactions({ store, apolloClient }: ProcessContext) { export default function reactions({ store, apolloClient }: ProcessContext) {
const { $calculation } = store; const { $calculation } = store;
@ -27,7 +30,9 @@ export default function reactions({ store, apolloClient }: ProcessContext) {
fireImmediately: true, fireImmediately: true,
} }
); );
const { getIrr } = helper({ apolloClient }); const { getIrr } = helper({ apolloClient });
reaction( reaction(
() => $calculation.$values.getValues(['product', 'tarif', 'bonusCoefficient']), () => $calculation.$values.getValues(['product', 'tarif', 'bonusCoefficient']),
async (values) => { async (values) => {

View File

@ -96,9 +96,12 @@ export function common({ store, apolloClient, queryClient }: ProcessContext) {
typePTS, typePTS,
objectRegistration, objectRegistration,
}) => { }) => {
if (objectRegistration === 100_000_001 && typePTS === 100_000_001) { if (!(objectRegistration === 100_000_001 && typePTS === 100_000_001) || !leaseObjectTypeId) {
$calculation.element('selectObjectCategoryTax').unblock(); $calculation.element('selectObjectCategoryTax').resetOptions().resetValue().block();
if (leaseObjectTypeId) {
return;
}
const { const {
data: { evo_leasingobject_type }, data: { evo_leasingobject_type },
} = await apolloClient.query({ } = await apolloClient.query({
@ -112,14 +115,12 @@ export function common({ store, apolloClient, queryClient }: ProcessContext) {
selectObjectCategoryTax.filter((option) => selectObjectCategoryTax.filter((option) =>
evo_leasingobject_type?.evo_category_tr?.includes(option.value) evo_leasingobject_type?.evo_category_tr?.includes(option.value)
) )
); )
} else { .unblock();
$calculation.element('selectObjectCategoryTax').resetOptions();
}
}
} else {
$calculation.element('selectObjectCategoryTax').resetValue().block();
} }
},
{
fireImmediately: true,
} }
); );
@ -226,6 +227,7 @@ export function common({ store, apolloClient, queryClient }: ProcessContext) {
() => $process.has('LoadKP') () => $process.has('LoadKP')
); );
// Не дышать на реакцию
reaction( reaction(
() => () =>
$calculation.$values.getValues([ $calculation.$values.getValues([
@ -243,20 +245,18 @@ export function common({ store, apolloClient, queryClient }: ProcessContext) {
typePTS, typePTS,
leaseObjectCategory, leaseObjectCategory,
leaseObjectType, leaseObjectType,
// eslint-disable-next-line sonarjs/cognitive-complexity
}) => { }) => {
if (!objectRegionRegistrationId || !regionRegistrationId) {
$calculation.element('selectRegistration').resetValue();
return;
}
const currentDate = dayjs().utc(false).format('YYYY-MM-DD'); const currentDate = dayjs().utc(false).format('YYYY-MM-DD');
const { let evo_region: CRMTypes.GetRegionQuery['evo_region'];
data: { evo_region }, if (objectRegionRegistrationId) {
} = await apolloClient.query({ const { data } = await apolloClient.query({
query: CRMTypes.GetRegionDocument, query: CRMTypes.GetRegionDocument,
variables: { regionId: objectRegionRegistrationId }, variables: { regionId: objectRegionRegistrationId },
}); });
evo_region = data.evo_region;
}
const { const {
data: { evo_addproduct_types }, data: { evo_addproduct_types },
@ -265,27 +265,50 @@ export function common({ store, apolloClient, queryClient }: ProcessContext) {
variables: { currentDate }, variables: { currentDate },
}); });
const options = evo_addproduct_types?.filter( const options = evo_addproduct_types?.filter((x) => {
(x) => if (
x?.evo_leasingobject_types?.find( !x?.evo_leasingobject_types?.find(
(evo_leasingobject_type) => (evo_leasingobject_type) =>
evo_leasingobject_type?.evo_leasingobject_typeid === leaseObjectType evo_leasingobject_type?.evo_leasingobject_typeid === leaseObjectType
) &&
x.evo_whom_register === objectRegistration &&
Boolean(
leaseObjectCategory === 100_000_001
? x.evo_towtruck === true || x.evo_towtruck === false
: x.evo_towtruck === false
) &&
x.evo_gibdd_region === (objectRegionRegistrationId === regionRegistrationId) &&
Boolean(typePTS && x.evo_pts_type?.includes(typePTS)) &&
Boolean(
x.evo_accountid &&
evo_region?.accounts?.some(
(evo_region_account) => evo_region_account?.accountid === x.evo_accountid
) )
) ) {
); return false;
}
if (!objectRegionRegistrationId && !regionRegistrationId) {
return false;
}
if (!(x?.evo_whom_register === objectRegistration)) {
return false;
}
if (leaseObjectCategory !== 100_000_001 && x?.evo_towtruck) {
return false;
}
if (!(x?.evo_gibdd_region === (objectRegionRegistrationId === regionRegistrationId))) {
return false;
}
if (
(typePTS &&
(!x?.evo_pts_type ||
x?.evo_pts_type.filter((evo_pts_type) => evo_pts_type > 0).length === 0)) ||
(typePTS &&
x?.evo_pts_type?.filter((evo_pts_type) => evo_pts_type > 0).includes(typePTS) === false)
) {
return false;
}
if (!x?.evo_accountid) {
return true;
}
return evo_region?.accounts
?.map((evo_region_account) => evo_region_account?.accountid)
.includes(x.evo_accountid);
});
$calculation.element('selectRegistration').setOptions(normalizeOptions(options)); $calculation.element('selectRegistration').setOptions(normalizeOptions(options));
} }
@ -473,6 +496,22 @@ export function common({ store, apolloClient, queryClient }: ProcessContext) {
} }
} }
); );
reaction(
() => $calculation.element('radioObjectRegistration').getValue(),
(objectRegistration) => {
if (objectRegistration === 100_000_000) {
$calculation.element('tbxVehicleTaxInYear').resetValue().block();
$calculation.element('radioTypePTS').resetValue().block();
} else {
$calculation.element('tbxVehicleTaxInYear').unblock();
$calculation.element('radioTypePTS').unblock();
}
},
{
fireImmediately: true,
}
);
} }
const key = uid(7); const key = uid(7);

View File

@ -8,6 +8,7 @@ import { gql } from '@apollo/client';
const { DEFAULT_FINGAP_ROW, DEFAULT_KASKO_ROW, DEFAULT_OSAGO_ROW } = insuranceTable; const { DEFAULT_FINGAP_ROW, DEFAULT_KASKO_ROW, DEFAULT_OSAGO_ROW } = insuranceTable;
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const QUERY_GET_QUOTE_INSURANCE_DATA = gql` const QUERY_GET_QUOTE_INSURANCE_DATA = gql`
query GetQuoteInsuranceData($quoteId: Uuid!) { query GetQuoteInsuranceData($quoteId: Uuid!) {
quote(quoteId: $quoteId) { quote(quoteId: $quoteId) {
@ -70,12 +71,12 @@ export async function getKPData({
}, },
values: { values: {
GPSBrand: quote?.evo_gps_brandid, GPSBrand: quote?.evo_gps_brandid,
GPSModel: quote?.evo_gps_modelid,
insAgeDrivers: quote?.evo_age_drivers ?? defaultValues.insAgeDrivers, insAgeDrivers: quote?.evo_age_drivers ?? defaultValues.insAgeDrivers,
insDecentral: quote?.evo_insurance_decentral ?? defaultValues.insDecentral, insDecentral: quote?.evo_insurance_decentral ?? defaultValues.insDecentral,
insExpDrivers: quote?.evo_exp_drivers ?? defaultValues.insExpDrivers, insExpDrivers: quote?.evo_exp_drivers ?? defaultValues.insExpDrivers,
insFranchise: quote?.evo_franchise ?? defaultValues.insFranchise, insFranchise: quote?.evo_franchise ?? defaultValues.insFranchise,
insUnlimitDrivers: quote?.evo_unlimit_drivers ?? defaultValues.insUnlimitDrivers, insUnlimitDrivers: quote?.evo_unlimit_drivers ?? defaultValues.insUnlimitDrivers,
model: quote?.evo_gps_modelid,
}, },
}; };
} }

View File

@ -267,40 +267,25 @@ export default function reactions({ store, apolloClient }: ProcessContext) {
); );
reaction( reaction(
() => $calculation.$values.getValues(['model', 'configuration']), () => $calculation.$values.getValues(['model']),
async ({ model: modelId, configuration: configurationId }) => { async ({ model: modelId }) => {
$calculation.element('labelDepreciationGroup').resetValue(); let evo_model: CRMTypes.GetModelQuery['evo_model'] = null;
if (configurationId) {
const {
data: { evo_equipment },
} = await apolloClient.query({
query: CRMTypes.GetConfigurationDocument,
variables: { configurationId },
});
if (evo_equipment?.evo_impairment_groupidData?.evo_name) {
$calculation
.element('labelDepreciationGroup')
.setValue(evo_equipment?.evo_impairment_groupidData?.evo_name);
return;
}
}
if (modelId) { if (modelId) {
const { const { data } = await apolloClient.query({
data: { evo_model },
} = await apolloClient.query({
query: CRMTypes.GetModelDocument, query: CRMTypes.GetModelDocument,
variables: { modelId }, variables: { modelId },
}); });
evo_model = data.evo_model;
}
if (evo_model?.evo_impairment_groupidData?.evo_name) { if (evo_model?.evo_impairment_groupidData?.evo_name) {
$calculation $calculation
.element('labelDepreciationGroup') .element('labelDepreciationGroup')
.setValue(evo_model?.evo_impairment_groupidData?.evo_name); .setValue(evo_model?.evo_impairment_groupidData?.evo_name);
} } else {
$calculation.element('labelDepreciationGroup').resetValue();
} }
} }
); );
@ -429,7 +414,7 @@ export default function reactions({ store, apolloClient }: ProcessContext) {
}); });
if (evo_leasingobject_type?.evo_id === '8') { if (evo_leasingobject_type?.evo_id === '8') {
$calculation.element('selectEngineType').resetValue().block(); $calculation.element('selectEngineType').setValue(null).block();
$calculation.element('tbxEngineVolume').resetValue().block(); $calculation.element('tbxEngineVolume').resetValue().block();
$calculation.element('tbxLeaseObjectMotorPower').resetValue().block(); $calculation.element('tbxLeaseObjectMotorPower').resetValue().block();
} else { } else {

View File

@ -9,8 +9,10 @@ export function common({ store, trpcClient }: ProcessContext) {
const { $calculation, $process, $tables } = store; const { $calculation, $process, $tables } = store;
reaction( reaction(
() => $calculation.element('selectQuote').getOption(), () => $calculation.$values.getValue('quote'),
(quote) => { () => {
const quote = $calculation.element('selectQuote').getOption();
if (!quote || $process.has('LoadKP')) return; if (!quote || $process.has('LoadKP')) return;
$process.add('LoadKP'); $process.add('LoadKP');

View File

@ -1,10 +1,12 @@
import type { GetQuoteInputData, GetQuoteProcessData } from '../load-kp/types'; import type { GetQuoteInputData, GetQuoteProcessData } from '../load-kp/types';
import { generateDegressionRows } from './lib/degression-tools';
import initializeApollo from '@/apollo/client'; import initializeApollo from '@/apollo/client';
import defaultValues from '@/config/default-values'; import defaultValues from '@/config/default-values';
import * as CRMTypes from '@/graphql/crm.types'; import * as CRMTypes from '@/graphql/crm.types';
import { gql } from '@apollo/client'; import { gql } from '@apollo/client';
import { sort } from 'radash'; import { sort } from 'radash';
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const QUERY_GET_QUOTE_PAYMENTS_DATA = gql` const QUERY_GET_QUOTE_PAYMENTS_DATA = gql`
query GetQuotePaymentsData($quoteId: Uuid!) { query GetQuotePaymentsData($quoteId: Uuid!) {
quote(quoteId: $quoteId) { quote(quoteId: $quoteId) {
@ -27,6 +29,7 @@ const QUERY_GET_QUOTE_PAYMENTS_DATA = gql`
} }
`; `;
// eslint-disable-next-line sonarjs/cognitive-complexity
export async function getKPData({ export async function getKPData({
values: { quote: quoteId, recalcWithRevision }, values: { quote: quoteId, recalcWithRevision },
}: GetQuoteInputData): Promise<GetQuoteProcessData> { }: GetQuoteInputData): Promise<GetQuoteProcessData> {
@ -45,7 +48,13 @@ export async function getKPData({
? Math.min(quote?.evo_period ?? 0, quote?.evo_accept_period ?? 0) ? Math.min(quote?.evo_period ?? 0, quote?.evo_accept_period ?? 0)
: quote?.evo_period ?? 0; : quote?.evo_period ?? 0;
const graphType = quote?.evo_graph_type ?? defaultValues.graphType;
const firstPaymentPerc = quote?.evo_first_payment_perc ?? defaultValues.firstPaymentPerc;
const lastPaymentPerc = quote?.evo_last_payment_perc ?? defaultValues.lastPaymentPerc;
const seasonType = quote?.evo_seasons_type;
let paymentsValues: number[] = []; let paymentsValues: number[] = [];
if (quote?.evo_graphs) { if (quote?.evo_graphs) {
paymentsValues = paymentsValues =
sort(quote?.evo_graphs, (evo_graph) => Date.parse(evo_graph?.createdon ?? '0')) sort(quote?.evo_graphs, (evo_graph) => Date.parse(evo_graph?.createdon ?? '0'))
@ -55,6 +64,19 @@ export async function getKPData({
.map((payment) => payment?.evo_payment_ratio || 0) || []; .map((payment) => payment?.evo_payment_ratio || 0) || [];
} }
const isDegression =
graphType === 100_000_001 &&
seasonType !== null &&
seasonType !== undefined &&
seasonType !== 100_000_007;
if (recalcWithRevision && isDegression) {
paymentsValues = generateDegressionRows({
leasingPeriod,
seasonType,
}).map((x) => x.value);
}
return { return {
payments: { payments: {
values: [ values: [
@ -64,14 +86,14 @@ export async function getKPData({
], ],
}, },
values: { values: {
firstPaymentPerc: quote?.evo_first_payment_perc ?? defaultValues.firstPaymentPerc, firstPaymentPerc,
graphType: quote?.evo_graph_type ?? defaultValues.graphType, graphType,
highSeasonStart: quote?.evo_high_season, highSeasonStart: quote?.evo_high_season,
lastPaymentPerc: quote?.evo_last_payment_perc ?? defaultValues.lastPaymentPerc, lastPaymentPerc,
leasingPeriod, leasingPeriod,
parmentsDecreasePercent: parmentsDecreasePercent:
quote?.evo_payments_decrease_perc ?? defaultValues.parmentsDecreasePercent, quote?.evo_payments_decrease_perc ?? defaultValues.parmentsDecreasePercent,
seasonType: quote?.evo_seasons_type, seasonType,
}, },
}; };
} }

View File

@ -0,0 +1,75 @@
import type { CalculationValues } from '@/stores/calculation/values/types';
import type { Row } from '@/stores/tables/payments/types';
const degressionSteps: { [key: number]: 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],
};
export function generateDegressionRows({
seasonType: degressionType,
leasingPeriod,
}: Pick<CalculationValues, 'leasingPeriod' | 'seasonType'>) {
let middlePayments: Row[] = [];
switch (degressionType) {
case 100_000_007: {
const editablePayments: Row[] = Array.from(
{
length: leasingPeriod - 3,
},
() => ({
status: 'Default',
value: 100,
})
);
middlePayments = [
{
status: 'Disabled',
value: 100,
},
...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);
middlePayments = Array.from(
{
length: leasingPeriod - 2,
},
(_v, i) => {
let value = step3;
if (i <= paymentsInStep * 2 - 1) {
value = step2;
}
if (i <= paymentsInStep - 1) {
value = step1;
}
return {
status: 'Disabled',
value,
};
}
);
break;
}
default: {
break;
}
}
return middlePayments;
}

View File

@ -1,3 +1,4 @@
import * as degressionTools from '../lib/degression-tools';
import * as seasonsConstants from '../lib/seasons-constants'; import * as seasonsConstants from '../lib/seasons-constants';
import * as seasonsTools from '../lib/seasons-tools'; import * as seasonsTools from '../lib/seasons-tools';
import { selectHighSeasonStart } from '@/config/default-options'; import { selectHighSeasonStart } from '@/config/default-options';
@ -244,85 +245,15 @@ export default function reactions({ store }: ProcessContext) {
// } // }
// ); // );
const degressionSteps: { [key: number]: number[] } = { makeDisposable(
100_000_003: [100, 50, 25], () =>
100_000_004: [100, 30, 10],
100_000_005: [100, 70, 40],
100_000_006: [100, 7, 3],
};
reaction( reaction(
() => { () => $calculation.$values.getValues(['leasingPeriod', 'seasonType']),
const degressionType = $calculation.element('selectSeasonType').getValue(); ({ seasonType, leasingPeriod }) => {
const leasingPeriod = $calculation.element('tbxLeasingPeriod').getValue(); const middlePayments: Row[] = degressionTools.generateDegressionRows({
const graphType = $calculation.element('radioGraphType').getValue();
return {
degressionType,
graphType,
leasingPeriod, leasingPeriod,
}; seasonType,
}, });
({ degressionType, leasingPeriod, graphType }) => {
if (graphType === 100_000_001) {
let middlePayments: Row[] = [];
switch (degressionType) {
case 100_000_007: {
const editablePayments: Row[] = Array.from(
{
length: leasingPeriod - 3,
},
() => ({
status: 'Default',
value: 100,
})
);
middlePayments = [
{
status: 'Disabled',
value: 100,
},
...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);
middlePayments = Array.from(
{
length: leasingPeriod - 2,
},
(_v, i) => {
let value = step3;
if (i <= paymentsInStep * 2 - 1) {
value = step2;
}
if (i <= paymentsInStep - 1) {
value = step1;
}
return {
status: 'Disabled',
value,
};
}
);
break;
}
default: {
break;
}
}
const firstPaymentPerc = $calculation.element('tbxFirstPaymentPerc').getValue(); const firstPaymentPerc = $calculation.element('tbxFirstPaymentPerc').getValue();
const lastPaymentPerc = $calculation.element('tbxLastPaymentPerc').getValue(); const lastPaymentPerc = $calculation.element('tbxLastPaymentPerc').getValue();
@ -345,7 +276,8 @@ export default function reactions({ store }: ProcessContext) {
$tables.payments.setStatuses(rows.map((row) => row.status)); $tables.payments.setStatuses(rows.map((row) => row.status));
} }
} ),
() => $calculation.element('radioGraphType').getValue() !== 100_000_001
); );
makeDisposable( makeDisposable(
@ -532,11 +464,16 @@ export default function reactions({ store }: ProcessContext) {
() => $process.has('LoadKP') () => $process.has('LoadKP')
); );
makeDisposable(
() =>
reaction( reaction(
() => $calculation.element('radioGraphType').getValue(), () => $calculation.element('radioGraphType').getValue(),
() => { () => {
$calculation.element('selectSeasonType').resetValue(); $calculation.element('selectSeasonType').resetValue();
$calculation.element('selectHighSeasonStart').resetValue(); $calculation.element('selectHighSeasonStart').resetValue();
$calculation.element('tbxParmentsDecreasePercent').resetValue();
} }
),
() => $process.has('LoadKP')
); );
} }

View File

@ -2,7 +2,7 @@ import { VAT } from '@/constants/values';
import * as CRMTypes from '@/graphql/crm.types'; import * as CRMTypes from '@/graphql/crm.types';
import type { ProcessContext } from '@/process/types'; import type { ProcessContext } from '@/process/types';
import { reaction } from 'mobx'; import { reaction } from 'mobx';
import { makeDisposable } from 'tools'; import { makeDisposable, round } from 'tools';
export default function reactions({ store, apolloClient }: ProcessContext) { export default function reactions({ store, apolloClient }: ProcessContext) {
const { $calculation, $process } = store; const { $calculation, $process } = store;
@ -40,9 +40,14 @@ export default function reactions({ store, apolloClient }: ProcessContext) {
reaction( reaction(
() => $calculation.$values.getValues(['leaseObjectPrice', 'supplierDiscountRub']), () => $calculation.$values.getValues(['leaseObjectPrice', 'supplierDiscountRub']),
({ leaseObjectPrice, supplierDiscountRub }) => { ({ leaseObjectPrice, supplierDiscountRub }) => {
// NaN fix
if (leaseObjectPrice === 0) {
$calculation.element('tbxSupplierDiscountPerc').resetValue();
} else {
$calculation $calculation
.element('tbxSupplierDiscountPerc') .element('tbxSupplierDiscountPerc')
.setValue((supplierDiscountRub / leaseObjectPrice) * 100); .setValue((supplierDiscountRub / leaseObjectPrice) * 100);
}
}, },
{ {
fireImmediately: true, fireImmediately: true,
@ -90,7 +95,7 @@ export default function reactions({ store, apolloClient }: ProcessContext) {
.element('tbxLeaseObjectPriceWthtVAT') .element('tbxLeaseObjectPriceWthtVAT')
.setValue(leaseObjectPrice - VATInLeaseObjectPrice); .setValue(leaseObjectPrice - VATInLeaseObjectPrice);
} else { } else {
const priceWithVAT = leaseObjectPrice / (1 + VAT); const priceWithVAT = round(leaseObjectPrice / (1 + VAT), 2);
const priceVAT = leaseObjectPrice - priceWithVAT; const priceVAT = leaseObjectPrice - priceWithVAT;
$calculation.element('tbxLeaseObjectPriceWthtVAT').setValue(priceWithVAT); $calculation.element('tbxLeaseObjectPriceWthtVAT').setValue(priceWithVAT);
$calculation.element('tbxVATInLeaseObjectPrice').setValue(priceVAT); $calculation.element('tbxVATInLeaseObjectPrice').setValue(priceVAT);
@ -128,9 +133,15 @@ export default function reactions({ store, apolloClient }: ProcessContext) {
); );
reaction( reaction(
() => $calculation.$values.getValues(['comissionPerc', 'plPriceRub']), () =>
({ plPriceRub, comissionPerc }) => { $calculation.$values.getValues([
const rub = (comissionPerc * plPriceRub) / 100; 'comissionPerc',
'plPriceRub',
'addEquipmentPrice',
'importProgramSum',
]),
({ plPriceRub, comissionPerc, addEquipmentPrice, importProgramSum }) => {
const rub = (comissionPerc * (plPriceRub + addEquipmentPrice - importProgramSum)) / 100;
$calculation.element('tbxComissionRub').setValue(rub); $calculation.element('tbxComissionRub').setValue(rub);
} }
); );
@ -140,8 +151,9 @@ export default function reactions({ store, apolloClient }: ProcessContext) {
reaction( reaction(
() => $calculation.element('tbxComissionRub').getValue(), () => $calculation.element('tbxComissionRub').getValue(),
(comissionRub) => { (comissionRub) => {
const { plPriceRub } = $calculation.$values.getValues(); const { plPriceRub, addEquipmentPrice, importProgramSum } =
const perc = (comissionRub / plPriceRub) * 100; $calculation.$values.getValues();
const perc = (comissionRub / (plPriceRub + addEquipmentPrice - importProgramSum)) * 100;
$calculation.element('tbxComissionPerc').setValue(perc); $calculation.element('tbxComissionPerc').setValue(perc);
} }
), ),
@ -157,28 +169,40 @@ export default function reactions({ store, apolloClient }: ProcessContext) {
'lastPaymentPerc', 'lastPaymentPerc',
'addEquipmentPrice', 'addEquipmentPrice',
'importProgramSum', 'importProgramSum',
'lastPaymentRule',
]), ]),
({ addEquipmentPrice, lastPaymentPerc, plPriceRub, importProgramSum }) => { ({ addEquipmentPrice, lastPaymentPerc, plPriceRub, importProgramSum, lastPaymentRule }) => {
if (lastPaymentRule === 100_000_000) {
return;
}
const rub = (lastPaymentPerc * (plPriceRub + addEquipmentPrice - importProgramSum)) / 100; const rub = (lastPaymentPerc * (plPriceRub + addEquipmentPrice - importProgramSum)) / 100;
$calculation.element('tbxLastPaymentRub').setValue(rub); $calculation.element('tbxLastPaymentRub').setValue(rub);
} }
), ),
() => $calculation.element('radioLastPaymentRule').getValue() === 100_000_000 () => $process.has('LoadKP')
); );
makeDisposable( makeDisposable(
() => () =>
reaction( reaction(
() => $calculation.element('tbxLastPaymentRub').getValue(), () =>
(lastPaymentRub) => { $calculation.$values.getValues([
const { plPriceRub, addEquipmentPrice, importProgramSum } = 'plPriceRub',
$calculation.$values.getValues(); 'lastPaymentRub',
'addEquipmentPrice',
'importProgramSum',
'lastPaymentRule',
]),
({ lastPaymentRub, plPriceRub, addEquipmentPrice, importProgramSum, lastPaymentRule }) => {
if (lastPaymentRule === 100_000_001) {
return;
}
const perc = (lastPaymentRub / (plPriceRub + addEquipmentPrice - importProgramSum)) * 100; const perc = (lastPaymentRub / (plPriceRub + addEquipmentPrice - importProgramSum)) * 100;
$calculation.element('tbxLastPaymentPerc').setValue(perc); $calculation.element('tbxLastPaymentPerc').setValue(perc);
} }
), ),
() => () => $process.has('LoadKP')
$process.has('LoadKP') ||
$calculation.element('radioLastPaymentRule').getValue() === 100_000_001
); );
} }

View File

@ -90,10 +90,8 @@ export function common({ store, apolloClient }: ProcessContext) {
* иначе открыты для редактирования * иначе открыты для редактирования
*/ */
reaction( reaction(
() => $calculation.$values.getValues(['recalcWithRevision', 'quote']), () => $calculation.$values.getValues(['recalcWithRevision', 'product']),
async ({ recalcWithRevision }) => { async ({ recalcWithRevision, product: productId }) => {
const productId = $calculation.element('selectProduct').getValue();
if (!productId) { if (!productId) {
$calculation.element('tbxFirstPaymentPerc').unblock(); $calculation.element('tbxFirstPaymentPerc').unblock();
$calculation.element('tbxFirstPaymentRub').unblock(); $calculation.element('tbxFirstPaymentRub').unblock();
@ -184,25 +182,25 @@ export function common({ store, apolloClient }: ProcessContext) {
reaction( reaction(
() => $calculation.$values.getValues(['leaseObjectUsed', 'subsidy', 'product', 'dealer']), () => $calculation.$values.getValues(['leaseObjectUsed', 'subsidy', 'product', 'dealer']),
async ({ leaseObjectUsed, subsidy, product: productId, dealer: dealerId }) => { async ({ leaseObjectUsed, subsidy, product: productId, dealer: dealerId }) => {
if (!productId || !dealerId) { let evo_baseproduct: CRMTypes.GetProductQuery['evo_baseproduct'] = null;
$calculation.element('radioDeliveryTime').unblock(); let dealer: CRMTypes.GetDealerQuery['dealer'] = null;
return; if (productId) {
} const { data } = await apolloClient.query({
const {
data: { evo_baseproduct },
} = await apolloClient.query({
query: CRMTypes.GetProductDocument, query: CRMTypes.GetProductDocument,
variables: { productId }, variables: { productId },
}); });
const { ({ evo_baseproduct } = data);
data: { dealer }, }
} = await apolloClient.query({
if (dealerId) {
const { data } = await apolloClient.query({
query: CRMTypes.GetDealerDocument, query: CRMTypes.GetDealerDocument,
variables: { dealerId }, variables: { dealerId },
}); });
({ dealer } = data);
}
if ( if (
leaseObjectUsed || leaseObjectUsed ||

View File

@ -48,13 +48,22 @@ export function createValidationSchema({ apolloClient }: ValidationContext) {
} }
} }
if (leaseObjectUsed && !mileage) { if (leaseObjectUsed) {
if (!mileage) {
ctx.addIssue({ ctx.addIssue({
code: z.ZodIssueCode.custom, code: z.ZodIssueCode.custom,
message: 'Не заполнено поле', message: 'Не заполнено поле',
path: ['tbxMileage'], path: ['tbxMileage'],
}); });
} }
if (!vin) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: 'Не заполнено поле',
path: ['tbxVIN'],
});
}
}
/** /**
* Если "Категория" содержит данные, то должны быть доступными для набора только арабские цифры и буквы латинского алфавита за исключением I, O, Q, так как они сходны по начертанию с цифрами 1, 0, 9. Можно использовать регулярное выражение: "^[A-HJ-NPR-Za-hj-npr-z0-9]{17}$". * Если "Категория" содержит данные, то должны быть доступными для набора только арабские цифры и буквы латинского алфавита за исключением I, O, Q, так как они сходны по начертанию с цифрами 1, 0, 9. Можно использовать регулярное выражение: "^[A-HJ-NPR-Za-hj-npr-z0-9]{17}$".

View File

@ -71,10 +71,7 @@ export const quoteRouter = router({
].map(({ getKPData }) => getKPData(input)) ].map(({ getKPData }) => getKPData(input))
); );
const values = processData.reduce( const values = processData.reduce((obj, data) => ({ ...obj, ...data.values }), defaultValues);
(obj, data) => Object.assign(obj, data.values),
defaultValues
);
const payments = processData.find((x) => x.payments)?.payments ?? defaultPayments; const payments = processData.find((x) => x.payments)?.payments ?? defaultPayments;
const insurance = processData.find((x) => x.insurance)?.insurance ?? defaultInsurance; const insurance = processData.find((x) => x.insurance)?.insurance ?? defaultInsurance;
const fingap = processData.find((x) => x.fingap)?.fingap ?? defaultFingap; const fingap = processData.find((x) => x.fingap)?.fingap ?? defaultFingap;

View File

@ -26,7 +26,7 @@ export default class ValuesStore {
public setValues = (values: Partial<CalculationValues>) => { public setValues = (values: Partial<CalculationValues>) => {
(Object.keys(values) as Array<keyof CalculationValues>).forEach((valueName) => { (Object.keys(values) as Array<keyof CalculationValues>).forEach((valueName) => {
const value = values[valueName]; const value = values[valueName];
if (value !== null && value !== undefined) { if (value !== undefined) {
this.setValue(valueName, value); this.setValue(valueName, value);
} }
}); });

View File

@ -1,34 +1,10 @@
/* eslint-disable func-style */
export function parser(value?: string) { export function parser(value?: string) {
if (!value) return 0; if (!value) return 0;
// eslint-disable-next-line unicorn/prefer-string-replace-all, require-unicode-regexp const normalized = value.replaceAll(/\s/gu, '').replaceAll(',', '.');
const normalized = value.replace(/\s/g, '').replaceAll(',', '.');
return Number.parseFloat(normalized); return Number.parseFloat(normalized);
} }
export const formatter = (value?: number) =>
Intl.NumberFormat('ru', {
minimumFractionDigits: 2,
}).format(value || 0);
export const formatterExtra = (value?: number) =>
Intl.NumberFormat('ru', {
maximumFractionDigits: 6,
minimumFractionDigits: 2,
}).format(value || 0);
export const moneyFormatter = Intl.NumberFormat('ru', {
currency: 'RUB',
style: 'currency',
}).format;
export const percentFormatter = Intl.NumberFormat('ru', {
maximumFractionDigits: 2,
minimumFractionDigits: 2,
style: 'percent',
}).format;
export function round(value: number, precision: number = 0) { export function round(value: number, precision: number = 0) {
return Number.parseFloat(value.toFixed(precision)); return Number.parseFloat(value.toFixed(precision));

View File

@ -38,3 +38,20 @@ function InputNumber({
} }
export default InputNumber as FC<InputNumberProps>; export default InputNumber as FC<InputNumberProps>;
export function createFormatter(options: Intl.NumberFormatOptions) {
const format = Intl.NumberFormat('ru', options).format;
const defaultFormat = Intl.NumberFormat('ru').format;
return ((value, { userTyping }) => {
if (userTyping) {
if (options.minimumFractionDigits && options.minimumFractionDigits <= 2) {
return defaultFormat(value || 0);
}
return value || 0;
}
return format(value || 0);
}) as NonNullable<InputNumberProps['formatter']>;
}