diff --git a/apps/web/Components/Calculation/Form/Insurance/InsuranceTable/config.tsx b/apps/web/Components/Calculation/Form/Insurance/InsuranceTable/config.tsx index 8b9d71a..5b6ca90 100644 --- a/apps/web/Components/Calculation/Form/Insurance/InsuranceTable/config.tsx +++ b/apps/web/Components/Calculation/Form/Insurance/InsuranceTable/config.tsx @@ -3,8 +3,8 @@ import { buildOptionComponent, buildValueComponent } from './builders'; import type * as Insurance from './types'; import { MAX_INSURANCE } from '@/constants/values'; import type { ColumnsType } from 'antd/lib/table'; -import { formatter, parser } from 'tools/number'; -import InputNumber from 'ui/elements/InputNumber'; +import { parser } from 'tools/number'; +import InputNumber, { createFormatter } from 'ui/elements/InputNumber'; import Select from 'ui/elements/Select'; export const columns: ColumnsType = [ @@ -44,7 +44,7 @@ export const columns: ColumnsType = [ return ( = { tbxLeaseObjectPrice: { @@ -77,7 +80,7 @@ const props: Partial = { max: 50, precision: 4, parser, - formatter: formatterExtra, + formatter: createFormatter({ minimumFractionDigits: 4, maximumFractionDigits: 4 }), addonAfter: '%', }, tbxFirstPaymentRub: { @@ -95,7 +98,7 @@ const props: Partial = { step: 1, precision: 6, parser, - formatter: formatterExtra, + formatter: createFormatter({ minimumFractionDigits: 6, maximumFractionDigits: 6 }), addonAfter: '%', }, tbxLastPaymentRub: { @@ -203,7 +206,7 @@ const props: Partial = { step: 0.5, precision: 4, parser, - formatter: formatterExtra, + formatter: createFormatter({ minimumFractionDigits: 4, maximumFractionDigits: 4 }), addonAfter: 'л', }, tbxMaxMass: { @@ -363,7 +366,7 @@ const props: Partial = { step: 0.0001, precision: 6, parser, - formatter: formatterExtra, + formatter: createFormatter({ minimumFractionDigits: 6, maximumFractionDigits: 6 }), addonAfter: '%', }, linkDownloadKp: { diff --git a/apps/web/Components/Output/Results/config.ts b/apps/web/Components/Output/Results/config.ts index 0a4446b..a0f7848 100644 --- a/apps/web/Components/Output/Results/config.ts +++ b/apps/web/Components/Output/Results/config.ts @@ -1,5 +1,4 @@ import type { ResultValues } from '@/stores/results/types'; -import { moneyFormatter, percentFormatter } from 'tools'; export const id = 'output'; export const title = 'Результаты'; @@ -26,6 +25,17 @@ export const titles: Record = { 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 = { resultAB_FL: moneyFormatter, resultAB_UL: moneyFormatter, diff --git a/apps/web/process/calculate/reactions/common.ts b/apps/web/process/calculate/reactions/common.ts index 07fcc02..db14853 100644 --- a/apps/web/process/calculate/reactions/common.ts +++ b/apps/web/process/calculate/reactions/common.ts @@ -1,7 +1,10 @@ import helper from '../lib/helper'; import type { ProcessContext } from '@/process/types'; import { reaction } from 'mobx'; -import { formatter } from 'tools'; + +export const formatter = Intl.NumberFormat('ru', { + minimumFractionDigits: 2, +}).format; export default function reactions({ store, apolloClient }: ProcessContext) { const { $calculation } = store; @@ -27,7 +30,9 @@ export default function reactions({ store, apolloClient }: ProcessContext) { fireImmediately: true, } ); + const { getIrr } = helper({ apolloClient }); + reaction( () => $calculation.$values.getValues(['product', 'tarif', 'bonusCoefficient']), async (values) => { diff --git a/apps/web/process/gibdd/reactions.ts b/apps/web/process/gibdd/reactions.ts index 701f66a..5888121 100644 --- a/apps/web/process/gibdd/reactions.ts +++ b/apps/web/process/gibdd/reactions.ts @@ -96,30 +96,31 @@ export function common({ store, apolloClient, queryClient }: ProcessContext) { typePTS, objectRegistration, }) => { - if (objectRegistration === 100_000_001 && typePTS === 100_000_001) { - $calculation.element('selectObjectCategoryTax').unblock(); - if (leaseObjectTypeId) { - const { - data: { evo_leasingobject_type }, - } = await apolloClient.query({ - query: CRMTypes.GetLeaseObjectTypeDocument, - variables: { leaseObjectTypeId }, - }); - if (leaseObjectCategory && leaseObjectCategory === evo_leasingobject_type?.evo_category) { - $calculation - .element('selectObjectCategoryTax') - .setOptions( - selectObjectCategoryTax.filter((option) => - evo_leasingobject_type?.evo_category_tr?.includes(option.value) - ) - ); - } else { - $calculation.element('selectObjectCategoryTax').resetOptions(); - } - } - } else { - $calculation.element('selectObjectCategoryTax').resetValue().block(); + if (!(objectRegistration === 100_000_001 && typePTS === 100_000_001) || !leaseObjectTypeId) { + $calculation.element('selectObjectCategoryTax').resetOptions().resetValue().block(); + + return; } + + const { + data: { evo_leasingobject_type }, + } = await apolloClient.query({ + query: CRMTypes.GetLeaseObjectTypeDocument, + variables: { leaseObjectTypeId }, + }); + if (leaseObjectCategory && leaseObjectCategory === evo_leasingobject_type?.evo_category) { + $calculation + .element('selectObjectCategoryTax') + .setOptions( + selectObjectCategoryTax.filter((option) => + evo_leasingobject_type?.evo_category_tr?.includes(option.value) + ) + ) + .unblock(); + } + }, + { + fireImmediately: true, } ); @@ -226,6 +227,7 @@ export function common({ store, apolloClient, queryClient }: ProcessContext) { () => $process.has('LoadKP') ); + // Не дышать на реакцию reaction( () => $calculation.$values.getValues([ @@ -243,20 +245,18 @@ export function common({ store, apolloClient, queryClient }: ProcessContext) { typePTS, leaseObjectCategory, 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 { - data: { evo_region }, - } = await apolloClient.query({ - query: CRMTypes.GetRegionDocument, - variables: { regionId: objectRegionRegistrationId }, - }); + let evo_region: CRMTypes.GetRegionQuery['evo_region']; + if (objectRegionRegistrationId) { + const { data } = await apolloClient.query({ + query: CRMTypes.GetRegionDocument, + variables: { regionId: objectRegionRegistrationId }, + }); + evo_region = data.evo_region; + } const { data: { evo_addproduct_types }, @@ -265,27 +265,50 @@ export function common({ store, apolloClient, queryClient }: ProcessContext) { variables: { currentDate }, }); - const options = evo_addproduct_types?.filter( - (x) => - x?.evo_leasingobject_types?.find( + const options = evo_addproduct_types?.filter((x) => { + if ( + !x?.evo_leasingobject_types?.find( (evo_leasingobject_type) => 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)); } @@ -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); diff --git a/apps/web/process/insurance/get-kp-data.ts b/apps/web/process/insurance/get-kp-data.ts index f364f0a..eac1899 100644 --- a/apps/web/process/insurance/get-kp-data.ts +++ b/apps/web/process/insurance/get-kp-data.ts @@ -8,6 +8,7 @@ import { gql } from '@apollo/client'; 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` query GetQuoteInsuranceData($quoteId: Uuid!) { quote(quoteId: $quoteId) { @@ -70,12 +71,12 @@ export async function getKPData({ }, values: { GPSBrand: quote?.evo_gps_brandid, + GPSModel: quote?.evo_gps_modelid, insAgeDrivers: quote?.evo_age_drivers ?? defaultValues.insAgeDrivers, insDecentral: quote?.evo_insurance_decentral ?? defaultValues.insDecentral, insExpDrivers: quote?.evo_exp_drivers ?? defaultValues.insExpDrivers, insFranchise: quote?.evo_franchise ?? defaultValues.insFranchise, insUnlimitDrivers: quote?.evo_unlimit_drivers ?? defaultValues.insUnlimitDrivers, - model: quote?.evo_gps_modelid, }, }; } diff --git a/apps/web/process/leasing-object/reactions/common.ts b/apps/web/process/leasing-object/reactions/common.ts index 0cbce4e..f43bf6b 100644 --- a/apps/web/process/leasing-object/reactions/common.ts +++ b/apps/web/process/leasing-object/reactions/common.ts @@ -267,40 +267,25 @@ export default function reactions({ store, apolloClient }: ProcessContext) { ); reaction( - () => $calculation.$values.getValues(['model', 'configuration']), - async ({ model: modelId, configuration: configurationId }) => { - $calculation.element('labelDepreciationGroup').resetValue(); - - 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; - } - } + () => $calculation.$values.getValues(['model']), + async ({ model: modelId }) => { + let evo_model: CRMTypes.GetModelQuery['evo_model'] = null; if (modelId) { - const { - data: { evo_model }, - } = await apolloClient.query({ + const { data } = await apolloClient.query({ query: CRMTypes.GetModelDocument, variables: { modelId }, }); - if (evo_model?.evo_impairment_groupidData?.evo_name) { - $calculation - .element('labelDepreciationGroup') - .setValue(evo_model?.evo_impairment_groupidData?.evo_name); - } + evo_model = data.evo_model; + } + + if (evo_model?.evo_impairment_groupidData?.evo_name) { + $calculation + .element('labelDepreciationGroup') + .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') { - $calculation.element('selectEngineType').resetValue().block(); + $calculation.element('selectEngineType').setValue(null).block(); $calculation.element('tbxEngineVolume').resetValue().block(); $calculation.element('tbxLeaseObjectMotorPower').resetValue().block(); } else { diff --git a/apps/web/process/load-kp/reactions.ts b/apps/web/process/load-kp/reactions.ts index 56519d7..1e4805a 100644 --- a/apps/web/process/load-kp/reactions.ts +++ b/apps/web/process/load-kp/reactions.ts @@ -9,8 +9,10 @@ export function common({ store, trpcClient }: ProcessContext) { const { $calculation, $process, $tables } = store; reaction( - () => $calculation.element('selectQuote').getOption(), - (quote) => { + () => $calculation.$values.getValue('quote'), + () => { + const quote = $calculation.element('selectQuote').getOption(); + if (!quote || $process.has('LoadKP')) return; $process.add('LoadKP'); diff --git a/apps/web/process/payments/get-kp-data.ts b/apps/web/process/payments/get-kp-data.ts index 7d403be..69823ef 100644 --- a/apps/web/process/payments/get-kp-data.ts +++ b/apps/web/process/payments/get-kp-data.ts @@ -1,10 +1,12 @@ import type { GetQuoteInputData, GetQuoteProcessData } from '../load-kp/types'; +import { generateDegressionRows } from './lib/degression-tools'; import initializeApollo from '@/apollo/client'; import defaultValues from '@/config/default-values'; import * as CRMTypes from '@/graphql/crm.types'; import { gql } from '@apollo/client'; import { sort } from 'radash'; +// eslint-disable-next-line @typescript-eslint/no-unused-vars const QUERY_GET_QUOTE_PAYMENTS_DATA = gql` query GetQuotePaymentsData($quoteId: Uuid!) { 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({ values: { quote: quoteId, recalcWithRevision }, }: GetQuoteInputData): Promise { @@ -45,7 +48,13 @@ export async function getKPData({ ? Math.min(quote?.evo_period ?? 0, quote?.evo_accept_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[] = []; + if (quote?.evo_graphs) { paymentsValues = 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) || []; } + 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 { payments: { values: [ @@ -64,14 +86,14 @@ export async function getKPData({ ], }, values: { - firstPaymentPerc: quote?.evo_first_payment_perc ?? defaultValues.firstPaymentPerc, - graphType: quote?.evo_graph_type ?? defaultValues.graphType, + firstPaymentPerc, + graphType, highSeasonStart: quote?.evo_high_season, - lastPaymentPerc: quote?.evo_last_payment_perc ?? defaultValues.lastPaymentPerc, + lastPaymentPerc, leasingPeriod, parmentsDecreasePercent: quote?.evo_payments_decrease_perc ?? defaultValues.parmentsDecreasePercent, - seasonType: quote?.evo_seasons_type, + seasonType, }, }; } diff --git a/apps/web/process/payments/lib/degression-tools.ts b/apps/web/process/payments/lib/degression-tools.ts new file mode 100644 index 0000000..846b69c --- /dev/null +++ b/apps/web/process/payments/lib/degression-tools.ts @@ -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) { + 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; +} diff --git a/apps/web/process/payments/reactions/common.ts b/apps/web/process/payments/reactions/common.ts index d58a3bf..b9b1fd5 100644 --- a/apps/web/process/payments/reactions/common.ts +++ b/apps/web/process/payments/reactions/common.ts @@ -1,3 +1,4 @@ +import * as degressionTools from '../lib/degression-tools'; import * as seasonsConstants from '../lib/seasons-constants'; import * as seasonsTools from '../lib/seasons-tools'; import { selectHighSeasonStart } from '@/config/default-options'; @@ -244,108 +245,39 @@ export default function reactions({ store }: ProcessContext) { // } // ); - 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], - }; + makeDisposable( + () => + reaction( + () => $calculation.$values.getValues(['leasingPeriod', 'seasonType']), + ({ seasonType, leasingPeriod }) => { + const middlePayments: Row[] = degressionTools.generateDegressionRows({ + leasingPeriod, + seasonType, + }); - reaction( - () => { - const degressionType = $calculation.element('selectSeasonType').getValue(); - const leasingPeriod = $calculation.element('tbxLeasingPeriod').getValue(); - const graphType = $calculation.element('radioGraphType').getValue(); + const firstPaymentPerc = $calculation.element('tbxFirstPaymentPerc').getValue(); + const lastPaymentPerc = $calculation.element('tbxLastPaymentPerc').getValue(); - return { - degressionType, - graphType, - leasingPeriod, - }; - }, - ({ degressionType, leasingPeriod, graphType }) => { - if (graphType === 100_000_001) { - let middlePayments: Row[] = []; + const rows: Row[] = [ + { + status: 'Disabled', + value: firstPaymentPerc, + }, + ...middlePayments, + { + status: 'Disabled', + value: lastPaymentPerc, + }, + ]; - 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; + if (!$process.has('LoadKP')) { + $tables.payments.setValues(rows.map((row) => row.value)); } - 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; - } + $tables.payments.setStatuses(rows.map((row) => row.status)); } - - const firstPaymentPerc = $calculation.element('tbxFirstPaymentPerc').getValue(); - const lastPaymentPerc = $calculation.element('tbxLastPaymentPerc').getValue(); - - const rows: Row[] = [ - { - status: 'Disabled', - value: firstPaymentPerc, - }, - ...middlePayments, - { - status: 'Disabled', - value: lastPaymentPerc, - }, - ]; - - if (!$process.has('LoadKP')) { - $tables.payments.setValues(rows.map((row) => row.value)); - } - - $tables.payments.setStatuses(rows.map((row) => row.status)); - } - } + ), + () => $calculation.element('radioGraphType').getValue() !== 100_000_001 ); makeDisposable( @@ -532,11 +464,16 @@ export default function reactions({ store }: ProcessContext) { () => $process.has('LoadKP') ); - reaction( - () => $calculation.element('radioGraphType').getValue(), - () => { - $calculation.element('selectSeasonType').resetValue(); - $calculation.element('selectHighSeasonStart').resetValue(); - } + makeDisposable( + () => + reaction( + () => $calculation.element('radioGraphType').getValue(), + () => { + $calculation.element('selectSeasonType').resetValue(); + $calculation.element('selectHighSeasonStart').resetValue(); + $calculation.element('tbxParmentsDecreasePercent').resetValue(); + } + ), + () => $process.has('LoadKP') ); } diff --git a/apps/web/process/price/reactions/common.ts b/apps/web/process/price/reactions/common.ts index a7c53a4..771dc21 100644 --- a/apps/web/process/price/reactions/common.ts +++ b/apps/web/process/price/reactions/common.ts @@ -2,7 +2,7 @@ import { VAT } from '@/constants/values'; import * as CRMTypes from '@/graphql/crm.types'; import type { ProcessContext } from '@/process/types'; import { reaction } from 'mobx'; -import { makeDisposable } from 'tools'; +import { makeDisposable, round } from 'tools'; export default function reactions({ store, apolloClient }: ProcessContext) { const { $calculation, $process } = store; @@ -40,9 +40,14 @@ export default function reactions({ store, apolloClient }: ProcessContext) { reaction( () => $calculation.$values.getValues(['leaseObjectPrice', 'supplierDiscountRub']), ({ leaseObjectPrice, supplierDiscountRub }) => { - $calculation - .element('tbxSupplierDiscountPerc') - .setValue((supplierDiscountRub / leaseObjectPrice) * 100); + // NaN fix + if (leaseObjectPrice === 0) { + $calculation.element('tbxSupplierDiscountPerc').resetValue(); + } else { + $calculation + .element('tbxSupplierDiscountPerc') + .setValue((supplierDiscountRub / leaseObjectPrice) * 100); + } }, { fireImmediately: true, @@ -90,7 +95,7 @@ export default function reactions({ store, apolloClient }: ProcessContext) { .element('tbxLeaseObjectPriceWthtVAT') .setValue(leaseObjectPrice - VATInLeaseObjectPrice); } else { - const priceWithVAT = leaseObjectPrice / (1 + VAT); + const priceWithVAT = round(leaseObjectPrice / (1 + VAT), 2); const priceVAT = leaseObjectPrice - priceWithVAT; $calculation.element('tbxLeaseObjectPriceWthtVAT').setValue(priceWithVAT); $calculation.element('tbxVATInLeaseObjectPrice').setValue(priceVAT); @@ -128,9 +133,15 @@ export default function reactions({ store, apolloClient }: ProcessContext) { ); reaction( - () => $calculation.$values.getValues(['comissionPerc', 'plPriceRub']), - ({ plPriceRub, comissionPerc }) => { - const rub = (comissionPerc * plPriceRub) / 100; + () => + $calculation.$values.getValues([ + 'comissionPerc', + 'plPriceRub', + 'addEquipmentPrice', + 'importProgramSum', + ]), + ({ plPriceRub, comissionPerc, addEquipmentPrice, importProgramSum }) => { + const rub = (comissionPerc * (plPriceRub + addEquipmentPrice - importProgramSum)) / 100; $calculation.element('tbxComissionRub').setValue(rub); } ); @@ -140,8 +151,9 @@ export default function reactions({ store, apolloClient }: ProcessContext) { reaction( () => $calculation.element('tbxComissionRub').getValue(), (comissionRub) => { - const { plPriceRub } = $calculation.$values.getValues(); - const perc = (comissionRub / plPriceRub) * 100; + const { plPriceRub, addEquipmentPrice, importProgramSum } = + $calculation.$values.getValues(); + const perc = (comissionRub / (plPriceRub + addEquipmentPrice - importProgramSum)) * 100; $calculation.element('tbxComissionPerc').setValue(perc); } ), @@ -157,28 +169,40 @@ export default function reactions({ store, apolloClient }: ProcessContext) { 'lastPaymentPerc', 'addEquipmentPrice', 'importProgramSum', + 'lastPaymentRule', ]), - ({ addEquipmentPrice, lastPaymentPerc, plPriceRub, importProgramSum }) => { + ({ addEquipmentPrice, lastPaymentPerc, plPriceRub, importProgramSum, lastPaymentRule }) => { + if (lastPaymentRule === 100_000_000) { + return; + } + const rub = (lastPaymentPerc * (plPriceRub + addEquipmentPrice - importProgramSum)) / 100; $calculation.element('tbxLastPaymentRub').setValue(rub); } ), - () => $calculation.element('radioLastPaymentRule').getValue() === 100_000_000 + () => $process.has('LoadKP') ); makeDisposable( () => reaction( - () => $calculation.element('tbxLastPaymentRub').getValue(), - (lastPaymentRub) => { - const { plPriceRub, addEquipmentPrice, importProgramSum } = - $calculation.$values.getValues(); + () => + $calculation.$values.getValues([ + 'plPriceRub', + 'lastPaymentRub', + 'addEquipmentPrice', + 'importProgramSum', + 'lastPaymentRule', + ]), + ({ lastPaymentRub, plPriceRub, addEquipmentPrice, importProgramSum, lastPaymentRule }) => { + if (lastPaymentRule === 100_000_001) { + return; + } + const perc = (lastPaymentRub / (plPriceRub + addEquipmentPrice - importProgramSum)) * 100; $calculation.element('tbxLastPaymentPerc').setValue(perc); } ), - () => - $process.has('LoadKP') || - $calculation.element('radioLastPaymentRule').getValue() === 100_000_001 + () => $process.has('LoadKP') ); } diff --git a/apps/web/process/used-pl/reactions.ts b/apps/web/process/used-pl/reactions.ts index 3d38931..d0fcce3 100644 --- a/apps/web/process/used-pl/reactions.ts +++ b/apps/web/process/used-pl/reactions.ts @@ -90,10 +90,8 @@ export function common({ store, apolloClient }: ProcessContext) { * иначе открыты для редактирования */ reaction( - () => $calculation.$values.getValues(['recalcWithRevision', 'quote']), - async ({ recalcWithRevision }) => { - const productId = $calculation.element('selectProduct').getValue(); - + () => $calculation.$values.getValues(['recalcWithRevision', 'product']), + async ({ recalcWithRevision, product: productId }) => { if (!productId) { $calculation.element('tbxFirstPaymentPerc').unblock(); $calculation.element('tbxFirstPaymentRub').unblock(); @@ -184,25 +182,25 @@ export function common({ store, apolloClient }: ProcessContext) { reaction( () => $calculation.$values.getValues(['leaseObjectUsed', 'subsidy', 'product', 'dealer']), async ({ leaseObjectUsed, subsidy, product: productId, dealer: dealerId }) => { - if (!productId || !dealerId) { - $calculation.element('radioDeliveryTime').unblock(); + let evo_baseproduct: CRMTypes.GetProductQuery['evo_baseproduct'] = null; + let dealer: CRMTypes.GetDealerQuery['dealer'] = null; - return; + if (productId) { + const { data } = await apolloClient.query({ + query: CRMTypes.GetProductDocument, + variables: { productId }, + }); + + ({ evo_baseproduct } = data); } - const { - data: { evo_baseproduct }, - } = await apolloClient.query({ - query: CRMTypes.GetProductDocument, - variables: { productId }, - }); - - const { - data: { dealer }, - } = await apolloClient.query({ - query: CRMTypes.GetDealerDocument, - variables: { dealerId }, - }); + if (dealerId) { + const { data } = await apolloClient.query({ + query: CRMTypes.GetDealerDocument, + variables: { dealerId }, + }); + ({ dealer } = data); + } if ( leaseObjectUsed || diff --git a/apps/web/process/used-pl/validation.ts b/apps/web/process/used-pl/validation.ts index a926499..a14b65a 100644 --- a/apps/web/process/used-pl/validation.ts +++ b/apps/web/process/used-pl/validation.ts @@ -48,12 +48,21 @@ export function createValidationSchema({ apolloClient }: ValidationContext) { } } - if (leaseObjectUsed && !mileage) { - ctx.addIssue({ - code: z.ZodIssueCode.custom, - message: 'Не заполнено поле', - path: ['tbxMileage'], - }); + if (leaseObjectUsed) { + if (!mileage) { + ctx.addIssue({ + code: z.ZodIssueCode.custom, + message: 'Не заполнено поле', + path: ['tbxMileage'], + }); + } + if (!vin) { + ctx.addIssue({ + code: z.ZodIssueCode.custom, + message: 'Не заполнено поле', + path: ['tbxVIN'], + }); + } } /** diff --git a/apps/web/server/routers/quote/index.ts b/apps/web/server/routers/quote/index.ts index fb01861..999260f 100644 --- a/apps/web/server/routers/quote/index.ts +++ b/apps/web/server/routers/quote/index.ts @@ -71,10 +71,7 @@ export const quoteRouter = router({ ].map(({ getKPData }) => getKPData(input)) ); - const values = processData.reduce( - (obj, data) => Object.assign(obj, data.values), - defaultValues - ); + const values = processData.reduce((obj, data) => ({ ...obj, ...data.values }), defaultValues); const payments = processData.find((x) => x.payments)?.payments ?? defaultPayments; const insurance = processData.find((x) => x.insurance)?.insurance ?? defaultInsurance; const fingap = processData.find((x) => x.fingap)?.fingap ?? defaultFingap; diff --git a/apps/web/stores/calculation/values/index.ts b/apps/web/stores/calculation/values/index.ts index 15e738f..ea95c70 100644 --- a/apps/web/stores/calculation/values/index.ts +++ b/apps/web/stores/calculation/values/index.ts @@ -26,7 +26,7 @@ export default class ValuesStore { public setValues = (values: Partial) => { (Object.keys(values) as Array).forEach((valueName) => { const value = values[valueName]; - if (value !== null && value !== undefined) { + if (value !== undefined) { this.setValue(valueName, value); } }); diff --git a/packages/tools/number.ts b/packages/tools/number.ts index d6fc194..c3f7c7a 100644 --- a/packages/tools/number.ts +++ b/packages/tools/number.ts @@ -1,34 +1,10 @@ -/* eslint-disable func-style */ - export function parser(value?: string) { if (!value) return 0; - // eslint-disable-next-line unicorn/prefer-string-replace-all, require-unicode-regexp - const normalized = value.replace(/\s/g, '').replaceAll(',', '.'); + const normalized = value.replaceAll(/\s/gu, '').replaceAll(',', '.'); 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) { return Number.parseFloat(value.toFixed(precision)); diff --git a/packages/ui/elements/InputNumber.tsx b/packages/ui/elements/InputNumber.tsx index 583f8da..f15e4ab 100644 --- a/packages/ui/elements/InputNumber.tsx +++ b/packages/ui/elements/InputNumber.tsx @@ -38,3 +38,20 @@ function InputNumber({ } export default InputNumber as FC; + +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; +}