merge experimental/zod-validation
This commit is contained in:
parent
5748bb9ffd
commit
7fe8de83c8
@ -14,10 +14,10 @@ const Grid = styled(Flex)`
|
||||
const Validation = observer(() => {
|
||||
const store = useStore();
|
||||
|
||||
const messages = store.$tables.fingap.validation.getMessages();
|
||||
const errors = store.$tables.fingap.validation.getErrors();
|
||||
|
||||
if (messages?.length) {
|
||||
return <Alert type="error" banner message={messages[0]} />;
|
||||
if (errors?.length) {
|
||||
return <Alert type="error" banner message={errors[0].message} />;
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
@ -19,10 +19,10 @@ const TableWrapper = styled.div`
|
||||
const Validation = observer(() => {
|
||||
const store = useStore();
|
||||
|
||||
const messages = store.$tables.insurance.validation.getMessages();
|
||||
const errors = store.$tables.insurance.validation.getErrors();
|
||||
|
||||
if (messages?.length) {
|
||||
return <Alert type="error" banner message={messages[0]} />;
|
||||
if (errors?.length) {
|
||||
return <Alert type="error" banner message={errors[0].message} />;
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
@ -33,10 +33,10 @@ const Validation = observer(() => {
|
||||
const store = useStore();
|
||||
const { payments } = store.$tables;
|
||||
|
||||
const messages = payments.validation.getMessages();
|
||||
const errors = payments.validation.getErrors();
|
||||
|
||||
if (messages?.length) {
|
||||
return <Alert type="error" banner message={messages[0]} />;
|
||||
if (errors?.length) {
|
||||
return <Alert type="error" banner message={errors[0].message} />;
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
@ -25,12 +25,12 @@ const AlertWrapper = styled(Box)`
|
||||
|
||||
function getElementsErrors($calculation) {
|
||||
return Object.values($calculation.$validation).map((validation) => {
|
||||
const elementErrors = validation.getMessages();
|
||||
const elementErrors = validation.getErrors();
|
||||
const elementTitle = validation.params.err_title;
|
||||
|
||||
return elementErrors.map((error) => (
|
||||
return elementErrors.map(({ key, message }) => (
|
||||
<AlertWrapper>
|
||||
<Alert type="error" showIcon message={Message(elementTitle, error)} />
|
||||
<Alert key={key} type="error" showIcon message={Message(elementTitle, message)} />
|
||||
</AlertWrapper>
|
||||
));
|
||||
});
|
||||
@ -38,18 +38,22 @@ function getElementsErrors($calculation) {
|
||||
|
||||
function getPaymentsTableErrors($tables) {
|
||||
const { payments } = $tables;
|
||||
const messages = payments.validation.getMessages();
|
||||
const errors = payments.validation.getErrors();
|
||||
const title = payments.validation.params.err_title;
|
||||
|
||||
return messages.map((text) => <Alert type="error" showIcon message={Message(title, text)} />);
|
||||
return errors.map(({ key, message }) => (
|
||||
<Alert key={key} type="error" showIcon message={Message(title, message)} />
|
||||
));
|
||||
}
|
||||
|
||||
function getInsuranceTableErrors($tables) {
|
||||
const { insurance } = $tables;
|
||||
const messages = insurance.validation.getMessages();
|
||||
const errors = insurance.validation.getErrors();
|
||||
const title = insurance.validation.params.err_title;
|
||||
|
||||
return messages.map((text) => <Alert type="error" showIcon message={Message(title, text)} />);
|
||||
return errors.map(({ key, message }) => (
|
||||
<Alert key={key} type="error" showIcon message={Message(title, message)} />
|
||||
));
|
||||
}
|
||||
|
||||
const Errors = observer(() => {
|
||||
|
||||
@ -7,3 +7,4 @@ export const MAX_LEASING_PERIOD = 60;
|
||||
export const MIN_LASTPAYMENT_NSIB = 3500;
|
||||
export const MIN_PAYMENT = 3;
|
||||
export const VAT = 0.2;
|
||||
export const MAX_MASS = 3500;
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
/* eslint-disable @typescript-eslint/naming-convention */
|
||||
import type { ProcessContext } from '../types';
|
||||
import { normalizeOptions } from '@/../../packages/tools';
|
||||
import * as CRMTypes from '@/graphql/crm.types';
|
||||
import dayjs from 'dayjs';
|
||||
import utc from 'dayjs/plugin/utc';
|
||||
import { reaction } from 'mobx';
|
||||
import { normalizeOptions } from 'tools';
|
||||
|
||||
dayjs.extend(utc);
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/* eslint-disable @typescript-eslint/naming-convention */
|
||||
import type { ProcessContext } from '../../../types';
|
||||
import type { ValidationContext } from '../../types';
|
||||
import { getUser } from '@/api/user/query';
|
||||
import type { ElementsTypes } from '@/Components/Calculation/config/map/values';
|
||||
import { STALE_TIME } from '@/constants/request';
|
||||
@ -8,7 +8,7 @@ import dayjs from 'dayjs';
|
||||
|
||||
export type ProductId = ElementsTypes['selectProduct'];
|
||||
|
||||
export default function helper({ apolloClient, queryClient }: ProcessContext) {
|
||||
export default function helper({ apolloClient, queryClient }: ValidationContext) {
|
||||
return {
|
||||
async getCoefficient(productId: ProductId) {
|
||||
if (!productId) {
|
||||
@ -1,11 +1,11 @@
|
||||
/* eslint-disable @typescript-eslint/naming-convention */
|
||||
import type { ProcessContext } from '../../types';
|
||||
import helper from './lib/helper';
|
||||
import { makeDisposable } from '@/../../packages/tools';
|
||||
import helper from '../lib/helper';
|
||||
import * as CRMTypes from '@/graphql/crm.types';
|
||||
import dayjs from 'dayjs';
|
||||
import utc from 'dayjs/plugin/utc';
|
||||
import { reaction } from 'mobx';
|
||||
import { makeDisposable } from 'tools';
|
||||
|
||||
dayjs.extend(utc);
|
||||
|
||||
|
||||
@ -1,23 +1,35 @@
|
||||
import helper from './lib/helper';
|
||||
import { createValidationSchema } from '../validation';
|
||||
import type { Elements } from '@/Components/Calculation/config/map/values';
|
||||
import type { ProcessContext } from '@/process/types';
|
||||
import ValidationHelper from '@/stores/validation/helper';
|
||||
import { reaction } from 'mobx';
|
||||
import { round } from 'tools';
|
||||
import { uid } from 'radash';
|
||||
|
||||
const key = uid(7);
|
||||
|
||||
export default function reactions(context: ProcessContext) {
|
||||
const { store } = context;
|
||||
const { $calculation } = store;
|
||||
const { getCoefficient } = helper(context);
|
||||
const validationSchema = createValidationSchema(context);
|
||||
|
||||
const helper = new ValidationHelper();
|
||||
reaction(
|
||||
() => $calculation.$values.getValues(['product', 'saleBonus']),
|
||||
async ({ product: productId, saleBonus }) => {
|
||||
const coefficient = await getCoefficient(productId);
|
||||
const maxBonus = (coefficient?.evo_sot_coefficient || 0) * 100;
|
||||
async (values) => {
|
||||
helper.removeErrors();
|
||||
const validationResult = await validationSchema.safeParseAsync(values);
|
||||
|
||||
$calculation.element('tbxSaleBonus').validate({
|
||||
invalid: round(saleBonus, 2) > round(maxBonus, 2),
|
||||
message: 'Размер бонуса МПЛ не может быть выше установленного по СОТ',
|
||||
});
|
||||
if (!validationResult.success) {
|
||||
validationResult.error.errors.forEach(({ path, message }) => {
|
||||
(path as Elements[]).forEach((elementName) => {
|
||||
const removeError = $calculation.element(elementName).setError({ key, message });
|
||||
if (removeError) helper.add(removeError);
|
||||
});
|
||||
});
|
||||
}
|
||||
},
|
||||
{
|
||||
delay: 100,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
24
apps/web/process/bonuses/validation.ts
Normal file
24
apps/web/process/bonuses/validation.ts
Normal file
@ -0,0 +1,24 @@
|
||||
import type { ValidationContext } from '../types';
|
||||
import helper from './lib/helper';
|
||||
import ValuesSchema from '@/config/schema/values';
|
||||
import { round } from 'tools';
|
||||
import { z } from 'zod';
|
||||
|
||||
export function createValidationSchema(context: ValidationContext) {
|
||||
const { getCoefficient } = helper(context);
|
||||
|
||||
return ValuesSchema.pick({ product: true, saleBonus: true }).superRefine(
|
||||
async ({ product, saleBonus }, ctx) => {
|
||||
const coefficient = await getCoefficient(product);
|
||||
const maxBonus = (coefficient?.evo_sot_coefficient || 0) * 100;
|
||||
|
||||
if (round(saleBonus, 2) > round(maxBonus, 2)) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: 'Размер бонуса МПЛ не может быть выше установленного по СОТ',
|
||||
path: ['tbxSaleBonus'],
|
||||
});
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
@ -2,7 +2,9 @@ import types from '@/Components/Calculation/config/elements-types';
|
||||
import type * as Values from '@/Components/Calculation/config/map/values';
|
||||
import type * as Insurance from '@/Components/Calculation/Form/Insurance/InsuranceTable/types';
|
||||
import type { ProcessContext } from '@/process/types';
|
||||
import ValidationHelper from '@/stores/validation/helper';
|
||||
import { reaction } from 'mobx';
|
||||
import { uid } from 'radash';
|
||||
import type { BaseOption } from 'ui/elements/types';
|
||||
|
||||
function hasInvalidValueOrOptions(value: unknown, options: Array<BaseOption<unknown>>) {
|
||||
@ -21,6 +23,7 @@ export default function reactions({ store }: ProcessContext) {
|
||||
const hasElementsErrors = Object.values($calculation.$validation).some(
|
||||
(validation) => validation.hasErrors
|
||||
);
|
||||
|
||||
const hasPaymentsErrors = $tables.payments.validation.hasErrors;
|
||||
const hasInsuranceErrors = $tables.insurance.validation.hasErrors;
|
||||
const hasFingapErrors = $tables.fingap.validation.hasErrors;
|
||||
@ -44,6 +47,10 @@ export default function reactions({ store }: ProcessContext) {
|
||||
/**
|
||||
* Проверяем, что выбранное значение элемента есть в списке
|
||||
*/
|
||||
|
||||
const key = uid(7);
|
||||
const message = 'Выбранное значение отсутствует в списке';
|
||||
|
||||
function validateOptionsElement(elementName: Values.Elements) {
|
||||
const type = types[elementName];
|
||||
if (type().typeName !== 'Options') {
|
||||
@ -52,6 +59,8 @@ export default function reactions({ store }: ProcessContext) {
|
||||
|
||||
const element = $calculation.element(elementName);
|
||||
|
||||
const validationHelper = new ValidationHelper();
|
||||
|
||||
reaction(
|
||||
() => {
|
||||
const options = element.getOptions();
|
||||
@ -63,11 +72,16 @@ export default function reactions({ store }: ProcessContext) {
|
||||
};
|
||||
},
|
||||
({ value, options }) => {
|
||||
element.validate({
|
||||
invalid: hasInvalidValueOrOptions(value, options),
|
||||
message: 'Выбранное значение отсутствует в списке',
|
||||
silent: true,
|
||||
});
|
||||
if (hasInvalidValueOrOptions(value, options)) {
|
||||
const removeError = element.setError({
|
||||
key,
|
||||
message,
|
||||
silent: true,
|
||||
});
|
||||
if (removeError) validationHelper.add(removeError);
|
||||
} else {
|
||||
validationHelper.removeErrors();
|
||||
}
|
||||
},
|
||||
{
|
||||
delay: 100,
|
||||
@ -95,6 +109,8 @@ export default function reactions({ store }: ProcessContext) {
|
||||
function validateInsuranceCompany(rowKey: Insurance.Keys) {
|
||||
const row = $tables.insurance.row(rowKey);
|
||||
|
||||
const validationHelper = new ValidationHelper();
|
||||
|
||||
reaction(
|
||||
() => {
|
||||
const options = row.getOptions('insuranceCompany');
|
||||
@ -106,11 +122,16 @@ export default function reactions({ store }: ProcessContext) {
|
||||
};
|
||||
},
|
||||
({ value, options }) => {
|
||||
$tables.insurance.validate({
|
||||
invalid: hasInvalidValueOrOptions(value, options),
|
||||
message: 'Выбранное значение отсутствует в списке',
|
||||
silent: true,
|
||||
});
|
||||
if (hasInvalidValueOrOptions(value, options)) {
|
||||
const removeError = $tables.insurance.setError({
|
||||
key,
|
||||
message,
|
||||
silent: true,
|
||||
});
|
||||
validationHelper.add(removeError);
|
||||
} else {
|
||||
validationHelper.removeErrors();
|
||||
}
|
||||
},
|
||||
{
|
||||
delay: 100,
|
||||
|
||||
@ -1,44 +1,35 @@
|
||||
/* eslint-disable @typescript-eslint/naming-convention */
|
||||
import * as CRMTypes from '@/graphql/crm.types';
|
||||
import { createValidationSchema } from '../validation';
|
||||
import type { Elements } from '@/Components/Calculation/config/map/values';
|
||||
import type { ProcessContext } from '@/process/types';
|
||||
import ValidationHelper from '@/stores/validation/helper';
|
||||
import { reaction } from 'mobx';
|
||||
import { uid } from 'radash';
|
||||
|
||||
export default function reactions({ store, apolloClient }: ProcessContext) {
|
||||
const key = uid(7);
|
||||
|
||||
export default function reactions(context: ProcessContext) {
|
||||
const { store } = context;
|
||||
const { $calculation } = store;
|
||||
/**
|
||||
* На изменение поля Процет убывания платежей tbxParmentsDecreasePercent необходимо заложить проверку:
|
||||
* Если значение поля меньше значения в поле "Минимальный % убывания платежей" evo_min_decreasing_perc из записи,
|
||||
* указанной в поле ТарифselectTarif , то поле Процет убывания платежей tbxParmentsDecreasePercent должно обводиться красной рамкой
|
||||
* и выводиться сообщение "Процент убывания не может быть меньше минимального значения по данному тарифу
|
||||
* - <указывается значение из поля "Минимальный % убывания платежей">, иначе красная рамка снимается.
|
||||
* При красной рамке в данном поле нельзя осуществить расчет графика.
|
||||
*/
|
||||
const validationSchema = createValidationSchema(context);
|
||||
|
||||
const helper = new ValidationHelper();
|
||||
reaction(
|
||||
() => ({
|
||||
parmentsDecreasePercent: $calculation.element('tbxParmentsDecreasePercent').getValue(),
|
||||
tarifId: $calculation.element('selectTarif').getValue(),
|
||||
}),
|
||||
async ({ parmentsDecreasePercent, tarifId }) => {
|
||||
let evo_tarif: CRMTypes.GetTarifQuery['evo_tarif'] = null;
|
||||
() => $calculation.$values.getValues(['parmentsDecreasePercent', 'tarif']),
|
||||
async (values) => {
|
||||
helper.removeErrors();
|
||||
const validationResult = await validationSchema.safeParseAsync(values);
|
||||
|
||||
if (tarifId) {
|
||||
const { data } = await apolloClient.query({
|
||||
query: CRMTypes.GetTarifDocument,
|
||||
variables: {
|
||||
tarifId,
|
||||
},
|
||||
if (!validationResult.success) {
|
||||
validationResult.error.errors.forEach(({ path, message }) => {
|
||||
(path as Elements[]).forEach((elementName) => {
|
||||
const removeError = $calculation.element(elementName).setError({ key, message });
|
||||
if (removeError) helper.add(removeError);
|
||||
});
|
||||
});
|
||||
|
||||
({ evo_tarif } = data);
|
||||
}
|
||||
|
||||
$calculation.element('tbxParmentsDecreasePercent').validate({
|
||||
invalid: Boolean(
|
||||
evo_tarif?.evo_min_decreasing_perc &&
|
||||
parmentsDecreasePercent < evo_tarif?.evo_min_decreasing_perc
|
||||
),
|
||||
message: `Процент убывания не может быть меньше минимального значения по данному тарифу - ${evo_tarif?.evo_min_decreasing_perc}`,
|
||||
});
|
||||
},
|
||||
{
|
||||
delay: 100,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
41
apps/web/process/configurator/validation.ts
Normal file
41
apps/web/process/configurator/validation.ts
Normal file
@ -0,0 +1,41 @@
|
||||
/* eslint-disable @typescript-eslint/naming-convention */
|
||||
import type { ValidationContext } from '../types';
|
||||
import ValuesSchema from '@/config/schema/values';
|
||||
import * as CRMTypes from '@/graphql/crm.types';
|
||||
import { z } from 'zod';
|
||||
|
||||
export function createValidationSchema({ apolloClient }: ValidationContext) {
|
||||
return ValuesSchema.pick({ parmentsDecreasePercent: true, tarif: true }).superRefine(
|
||||
async ({ parmentsDecreasePercent, tarif: tarifId }, ctx) => {
|
||||
/**
|
||||
* На изменение поля Процет убывания платежей tbxParmentsDecreasePercent необходимо заложить проверку:
|
||||
* Если значение поля меньше значения в поле "Минимальный % убывания платежей" evo_min_decreasing_perc из записи,
|
||||
* указанной в поле ТарифselectTarif , то поле Процет убывания платежей tbxParmentsDecreasePercent должно обводиться красной рамкой
|
||||
* и выводиться сообщение "Процент убывания не может быть меньше минимального значения по данному тарифу
|
||||
* - <указывается значение из поля "Минимальный % убывания платежей">, иначе красная рамка снимается.
|
||||
* При красной рамке в данном поле нельзя осуществить расчет графика.
|
||||
*/
|
||||
if (tarifId) {
|
||||
const {
|
||||
data: { evo_tarif },
|
||||
} = await apolloClient.query({
|
||||
query: CRMTypes.GetTarifDocument,
|
||||
variables: {
|
||||
tarifId,
|
||||
},
|
||||
});
|
||||
|
||||
if (
|
||||
evo_tarif?.evo_min_decreasing_perc &&
|
||||
parmentsDecreasePercent < evo_tarif?.evo_min_decreasing_perc
|
||||
) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: `Процент убывания не может быть меньше минимального значения по данному тарифу - ${evo_tarif?.evo_min_decreasing_perc}`,
|
||||
path: ['tbxParmentsDecreasePercent'],
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
@ -1,9 +1,14 @@
|
||||
import type { ProcessContext } from '@/process/types';
|
||||
import ValidationHelper from '@/stores/validation/helper';
|
||||
import { reaction } from 'mobx';
|
||||
import { uid } from 'radash';
|
||||
|
||||
const key = uid(7);
|
||||
|
||||
export default function reactions({ store }: ProcessContext) {
|
||||
const { $tables } = store;
|
||||
|
||||
const helper = new ValidationHelper();
|
||||
reaction(
|
||||
() => {
|
||||
const hasPaymentsErrors = $tables.payments.validation.hasErrors;
|
||||
@ -15,10 +20,15 @@ export default function reactions({ store }: ProcessContext) {
|
||||
};
|
||||
},
|
||||
({ hasPaymentsErrors, finGAPInsuranceCompany }) => {
|
||||
$tables.fingap.validate({
|
||||
invalid: finGAPInsuranceCompany !== null && hasPaymentsErrors,
|
||||
message: 'Неверно заполнены платежи',
|
||||
});
|
||||
if (finGAPInsuranceCompany !== null && hasPaymentsErrors) {
|
||||
const removeError = $tables.fingap.setError({
|
||||
key,
|
||||
message: 'Неверно заполнены платежи',
|
||||
});
|
||||
helper.add(removeError);
|
||||
} else {
|
||||
helper.removeErrors();
|
||||
}
|
||||
|
||||
if (hasPaymentsErrors) {
|
||||
$tables.fingap.clear();
|
||||
|
||||
@ -1,7 +1,9 @@
|
||||
/* eslint-disable @typescript-eslint/naming-convention */
|
||||
import type { ProcessContext } from '../types';
|
||||
import helper from './lib/helper';
|
||||
import { createValidationSchema } from './validation';
|
||||
import { getTransTax } from '@/api/1c/query';
|
||||
import type { Elements } from '@/Components/Calculation/config/map/values';
|
||||
import { selectObjectCategoryTax } from '@/config/default-options';
|
||||
import { STALE_TIME } from '@/constants/request';
|
||||
import * as CRMTypes from '@/graphql/crm.types';
|
||||
@ -10,6 +12,7 @@ import type { QueryFunctionContext } from '@tanstack/react-query';
|
||||
import dayjs from 'dayjs';
|
||||
import utc from 'dayjs/plugin/utc';
|
||||
import { reaction } from 'mobx';
|
||||
import { uid } from 'radash';
|
||||
import { makeDisposable, normalizeOptions } from 'tools';
|
||||
|
||||
dayjs.extend(utc);
|
||||
@ -43,36 +46,6 @@ export function common({ store, apolloClient, queryClient }: ProcessContext) {
|
||||
}
|
||||
);
|
||||
|
||||
reaction(
|
||||
() => $calculation.$values.getValues(['objectRegistration', 'vehicleTaxInYear']),
|
||||
({ objectRegistration, vehicleTaxInYear }) => {
|
||||
if (objectRegistration === 100_000_001) {
|
||||
$calculation.element('tbxVehicleTaxInYear').unblock();
|
||||
} else {
|
||||
$calculation.element('tbxVehicleTaxInYear').resetValue().block();
|
||||
}
|
||||
$calculation.element('tbxVehicleTaxInYear').validate({
|
||||
invalid: objectRegistration === 100_000_001 && !(vehicleTaxInYear > 0),
|
||||
message: 'Значение должно быть больше 0',
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
reaction(
|
||||
() => $calculation.$values.getValues(['objectRegistration', 'typePTS']),
|
||||
({ objectRegistration, typePTS }) => {
|
||||
if (objectRegistration === 100_000_001) {
|
||||
$calculation.element('radioTypePTS').unblock();
|
||||
} else {
|
||||
$calculation.element('radioTypePTS').resetValue().block();
|
||||
}
|
||||
$calculation.element('radioTypePTS').validate({
|
||||
invalid: objectRegistration === 100_000_001 && !typePTS,
|
||||
message: 'Не заполнено поле',
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
makeDisposable(
|
||||
() =>
|
||||
reaction(
|
||||
@ -503,89 +476,41 @@ export function common({ store, apolloClient, queryClient }: ProcessContext) {
|
||||
);
|
||||
}
|
||||
|
||||
export function validation({ store, apolloClient }: ProcessContext) {
|
||||
const { $calculation } = store;
|
||||
const key = uid(7);
|
||||
|
||||
export function validation(context: ProcessContext) {
|
||||
const { store } = context;
|
||||
const { $calculation } = store;
|
||||
const validationSchema = createValidationSchema(context);
|
||||
|
||||
const validationHelper = new ValidationHelper();
|
||||
reaction(
|
||||
() => $calculation.$values.getValues(['leaseObjectCategory', 'maxMass']),
|
||||
({ leaseObjectCategory, maxMass }) => {
|
||||
$calculation.element('tbxMaxMass').validate({
|
||||
invalid: leaseObjectCategory === 100_000_001 && maxMass > 3500,
|
||||
message: 'При категории ТС = В Разрешенная макс.масса не может быть больше 3500 кг',
|
||||
});
|
||||
$calculation.element('tbxMaxMass').validate({
|
||||
invalid: leaseObjectCategory === 100_000_002 && maxMass <= 3500,
|
||||
message: 'При категории ТС = С Разрешенная макс.масса не может быть меньше 3500 кг',
|
||||
});
|
||||
() =>
|
||||
$calculation.$values.getValues([
|
||||
'leaseObjectCategory',
|
||||
'maxMass',
|
||||
'leaseObjectType',
|
||||
'typePTS',
|
||||
'objectRegistration',
|
||||
'objectCategoryTax',
|
||||
'insNSIB',
|
||||
'vehicleTaxInYear',
|
||||
]),
|
||||
async (values) => {
|
||||
validationHelper.removeErrors();
|
||||
const validationResult = await validationSchema.safeParseAsync(values);
|
||||
|
||||
if (!validationResult.success) {
|
||||
validationResult.error.errors.forEach(({ path, message }) => {
|
||||
(path as Elements[]).forEach((elementName) => {
|
||||
const removeError = $calculation.element(elementName).setError({ key, message });
|
||||
if (removeError) validationHelper.add(removeError);
|
||||
});
|
||||
});
|
||||
}
|
||||
},
|
||||
{
|
||||
delay: 100,
|
||||
}
|
||||
);
|
||||
|
||||
{
|
||||
const validationHelper = new ValidationHelper();
|
||||
reaction(
|
||||
() =>
|
||||
$calculation.$values.getValues([
|
||||
'typePTS',
|
||||
'objectRegistration',
|
||||
'objectCategoryTax',
|
||||
'leaseObjectType',
|
||||
]),
|
||||
async ({
|
||||
leaseObjectType: leaseObjectTypeId,
|
||||
typePTS,
|
||||
objectRegistration,
|
||||
objectCategoryTax,
|
||||
}) => {
|
||||
if (!leaseObjectTypeId) {
|
||||
validationHelper.removeErrors();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const {
|
||||
data: { evo_leasingobject_type },
|
||||
} = await apolloClient.query({
|
||||
query: CRMTypes.GetLeaseObjectTypeDocument,
|
||||
variables: { leaseObjectTypeId },
|
||||
});
|
||||
|
||||
$calculation.element('selectObjectCategoryTax').validate({
|
||||
helper: validationHelper,
|
||||
invalid:
|
||||
objectRegistration === 100_000_001 &&
|
||||
typePTS === 100_000_001 &&
|
||||
objectCategoryTax === null &&
|
||||
Boolean(evo_leasingobject_type?.evo_category_tr?.length),
|
||||
message: 'Необходимо из ЭПТС указать Категорию в соответствии с ТР ТС 018/2011',
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
{
|
||||
const validationHelper = new ValidationHelper();
|
||||
reaction(
|
||||
() => $calculation.$values.getValues(['leaseObjectType', 'insNSIB']),
|
||||
async ({ insNSIB, leaseObjectType: leaseObjectTypeId }) => {
|
||||
if (!leaseObjectTypeId) {
|
||||
validationHelper.removeErrors();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const {
|
||||
data: { evo_leasingobject_type },
|
||||
} = await apolloClient.query({
|
||||
query: CRMTypes.GetLeaseObjectTypeDocument,
|
||||
variables: { leaseObjectTypeId },
|
||||
});
|
||||
|
||||
$calculation.element('selectInsNSIB').validate({
|
||||
helper: validationHelper,
|
||||
invalid: evo_leasingobject_type?.evo_id === '11' && !insNSIB,
|
||||
message: 'Страхование НСИБ обязательно для мотоциклистов',
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
95
apps/web/process/gibdd/validation.ts
Normal file
95
apps/web/process/gibdd/validation.ts
Normal file
@ -0,0 +1,95 @@
|
||||
/* eslint-disable @typescript-eslint/naming-convention */
|
||||
import type { ValidationContext } from '../types';
|
||||
import ValuesSchema from '@/config/schema/values';
|
||||
import { MAX_MASS } from '@/constants/values';
|
||||
import * as CRMTypes from '@/graphql/crm.types';
|
||||
import { z } from 'zod';
|
||||
|
||||
export function createValidationSchema({ apolloClient }: ValidationContext) {
|
||||
return ValuesSchema.pick({
|
||||
insNSIB: true,
|
||||
leaseObjectCategory: true,
|
||||
leaseObjectType: true,
|
||||
maxMass: true,
|
||||
objectCategoryTax: true,
|
||||
objectRegistration: true,
|
||||
typePTS: true,
|
||||
vehicleTaxInYear: true,
|
||||
}).superRefine(
|
||||
async (
|
||||
{
|
||||
leaseObjectCategory,
|
||||
maxMass,
|
||||
leaseObjectType: leaseObjectTypeId,
|
||||
typePTS,
|
||||
objectRegistration,
|
||||
objectCategoryTax,
|
||||
insNSIB,
|
||||
vehicleTaxInYear,
|
||||
},
|
||||
ctx
|
||||
) => {
|
||||
if (leaseObjectCategory === 100_000_001 && maxMass > MAX_MASS) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: `При категории ТС = В Разрешенная макс.масса не может быть больше ${MAX_MASS} кг`,
|
||||
path: ['tbxMaxMass'],
|
||||
});
|
||||
}
|
||||
|
||||
if (leaseObjectCategory === 100_000_002 && maxMass <= MAX_MASS) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: `При категории ТС = С Разрешенная макс.масса не может быть меньше ${MAX_MASS} кг`,
|
||||
path: ['tbxMaxMass'],
|
||||
});
|
||||
}
|
||||
|
||||
if (leaseObjectTypeId) {
|
||||
const {
|
||||
data: { evo_leasingobject_type },
|
||||
} = await apolloClient.query({
|
||||
query: CRMTypes.GetLeaseObjectTypeDocument,
|
||||
variables: { leaseObjectTypeId },
|
||||
});
|
||||
|
||||
if (
|
||||
objectRegistration === 100_000_001 &&
|
||||
typePTS === 100_000_001 &&
|
||||
objectCategoryTax === null &&
|
||||
Boolean(evo_leasingobject_type?.evo_category_tr?.length)
|
||||
) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: 'Не заполнено поле',
|
||||
path: ['selectObjectCategoryTax'],
|
||||
});
|
||||
}
|
||||
|
||||
if (evo_leasingobject_type?.evo_id === '11' && !insNSIB) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: 'Страхование НСИБ обязательно для мотоциклистов',
|
||||
path: ['selectInsNSIB'],
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (objectRegistration === 100_000_001 && !(vehicleTaxInYear > 0)) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: 'Значение должно быть больше 0',
|
||||
path: ['tbxVehicleTaxInYear'],
|
||||
});
|
||||
}
|
||||
|
||||
if (objectRegistration === 100_000_001 && !typePTS) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: 'Не заполнено поле',
|
||||
path: ['radioTypePTS'],
|
||||
});
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
@ -1,14 +1,16 @@
|
||||
/* eslint-disable @typescript-eslint/naming-convention */
|
||||
import type { ProcessContext } from '../types';
|
||||
import { normalizeOptions } from '@/../../packages/tools';
|
||||
import type * as Insurance from '@/Components/Calculation/Form/Insurance/InsuranceTable/types';
|
||||
import { createValidationSchema } from './validation';
|
||||
import type { Elements } from '@/Components/Calculation/config/map/values';
|
||||
import { selectLeaseObjectUseFor } from '@/config/default-options';
|
||||
import * as CRMTypes from '@/graphql/crm.types';
|
||||
import ValidationHelper from '@/stores/validation/helper';
|
||||
import { reaction } from 'mobx';
|
||||
import { comparer, reaction, toJS } from 'mobx';
|
||||
import { uid } from 'radash';
|
||||
import { normalizeOptions } from 'tools';
|
||||
|
||||
export function common({ store, apolloClient }: ProcessContext) {
|
||||
const { $calculation, $tables } = store;
|
||||
const { $calculation } = store;
|
||||
|
||||
reaction(
|
||||
() => $calculation.element('selectGPSBrand').getValue(),
|
||||
@ -113,75 +115,52 @@ export function common({ store, apolloClient }: ProcessContext) {
|
||||
);
|
||||
}
|
||||
|
||||
export function validation({ store, apolloClient }: ProcessContext) {
|
||||
const { $calculation, $tables } = store;
|
||||
const key = uid(7);
|
||||
|
||||
{
|
||||
const validationHelper = new ValidationHelper();
|
||||
reaction(
|
||||
() => ({
|
||||
insTerm: $tables.insurance.row('kasko').getValue('insTerm'),
|
||||
...$calculation.$values.getValues(['leasingPeriod', 'recalcWithRevision', 'quote']),
|
||||
}),
|
||||
async ({ leasingPeriod, insTerm, recalcWithRevision, quote: quoteId }) => {
|
||||
if (!quoteId) {
|
||||
validationHelper.removeErrors();
|
||||
export function validation(context: ProcessContext) {
|
||||
const { $calculation, $tables } = context.store;
|
||||
|
||||
return;
|
||||
}
|
||||
const validationSchema = createValidationSchema(context);
|
||||
const helper = new ValidationHelper();
|
||||
|
||||
const {
|
||||
data: { quote },
|
||||
} = await apolloClient.query({
|
||||
query: CRMTypes.GetQuoteDocument,
|
||||
variables: { quoteId },
|
||||
});
|
||||
reaction(
|
||||
() => {
|
||||
const values = $calculation.$values.getValues([
|
||||
'leasingPeriod',
|
||||
'quote',
|
||||
'recalcWithRevision',
|
||||
]);
|
||||
|
||||
$tables.insurance.validate({
|
||||
helper: validationHelper,
|
||||
invalid:
|
||||
recalcWithRevision &&
|
||||
quote?.evo_one_year_insurance === true &&
|
||||
leasingPeriod > 15 &&
|
||||
insTerm === 100_000_001,
|
||||
message:
|
||||
'Срок страхования КАСКО должен быть 12 месяцев, т.к. оформляется Однолетний полис',
|
||||
return {
|
||||
insurance: {
|
||||
fingap: toJS($tables.insurance.row('fingap').getValues()),
|
||||
kasko: toJS($tables.insurance.row('kasko').getValues()),
|
||||
osago: toJS($tables.insurance.row('osago').getValues()),
|
||||
},
|
||||
...values,
|
||||
};
|
||||
},
|
||||
async (values) => {
|
||||
helper.removeErrors();
|
||||
const validationResult = await validationSchema.safeParseAsync(values);
|
||||
|
||||
if (!validationResult.success) {
|
||||
validationResult.error.errors.forEach(({ path, message }) => {
|
||||
(path as Array<Elements & 'insurance'>).forEach((elementName) => {
|
||||
if (elementName === 'insurance') {
|
||||
const removeError = $tables.insurance.setError({ key, message });
|
||||
if (removeError) helper.add(removeError);
|
||||
} else {
|
||||
const removeError = $calculation.element(elementName).setError({ key, message });
|
||||
if (removeError) helper.add(removeError);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
(['osago', 'kasko'] as Insurance.Keys[]).forEach((key) => {
|
||||
const validationHelper = new ValidationHelper();
|
||||
reaction(
|
||||
() => $tables.insurance.row(key).getValues(),
|
||||
({ insCost, insured, policyType, insuranceCompany, insTerm }) => {
|
||||
validationHelper.removeErrors();
|
||||
|
||||
$tables.insurance.validate({
|
||||
helper: validationHelper,
|
||||
invalid: insCost === 0 && insured === 100_000_001,
|
||||
message: `Укажите стоимость ${policyType}, включаемую в график`,
|
||||
});
|
||||
|
||||
$tables.insurance.validate({
|
||||
helper: validationHelper,
|
||||
invalid: insCost > 0 && !insuranceCompany,
|
||||
message: `Укажите страховую компанию ${policyType}`,
|
||||
});
|
||||
|
||||
$tables.insurance.validate({
|
||||
helper: validationHelper,
|
||||
invalid: insCost > 0 && !insTerm,
|
||||
message: `Укажите срок страхования ${policyType}`,
|
||||
});
|
||||
|
||||
$tables.insurance.validate({
|
||||
helper: validationHelper,
|
||||
invalid: insCost > 0 && !insured,
|
||||
message: `Укажите плательщика ${policyType}`,
|
||||
});
|
||||
}
|
||||
);
|
||||
});
|
||||
},
|
||||
{
|
||||
delay: 100,
|
||||
equals: comparer.structural,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
82
apps/web/process/insurance/validation.ts
Normal file
82
apps/web/process/insurance/validation.ts
Normal file
@ -0,0 +1,82 @@
|
||||
import type { ValidationContext } from '../types';
|
||||
import type * as Insurance from '@/Components/Calculation/Form/Insurance/InsuranceTable/types';
|
||||
import { RowSchema } from '@/config/schema/insurance';
|
||||
import ValuesSchema from '@/config/schema/values';
|
||||
import * as CRMTypes from '@/graphql/crm.types';
|
||||
import { z } from 'zod';
|
||||
|
||||
export function createValidationSchema({ apolloClient }: ValidationContext) {
|
||||
return ValuesSchema.pick({
|
||||
leasingPeriod: true,
|
||||
quote: true,
|
||||
recalcWithRevision: true,
|
||||
})
|
||||
.extend({
|
||||
insurance: z
|
||||
.object({
|
||||
fingap: RowSchema,
|
||||
kasko: RowSchema,
|
||||
osago: RowSchema,
|
||||
})
|
||||
.strict(),
|
||||
})
|
||||
.superRefine(async ({ leasingPeriod, recalcWithRevision, quote: quoteId, insurance }, ctx) => {
|
||||
if (quoteId) {
|
||||
const {
|
||||
data: { quote },
|
||||
} = await apolloClient.query({
|
||||
query: CRMTypes.GetQuoteDocument,
|
||||
variables: { quoteId },
|
||||
});
|
||||
if (
|
||||
recalcWithRevision &&
|
||||
quote?.evo_one_year_insurance === true &&
|
||||
leasingPeriod > 15 &&
|
||||
insurance.kasko.insTerm === 100_000_001
|
||||
) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message:
|
||||
'Срок страхования КАСКО должен быть 12 месяцев, т.к. оформляется Однолетний полис',
|
||||
path: ['insurance'],
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
(['osago', 'kasko'] as Insurance.Keys[]).forEach((key) => {
|
||||
const { insCost, insured, policyType, insuranceCompany, insTerm } = insurance[key];
|
||||
|
||||
if (insCost === 0 && insured === 100_000_001) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: `Укажите стоимость ${policyType}, включаемую в график`,
|
||||
path: ['insurance'],
|
||||
});
|
||||
}
|
||||
|
||||
if (insCost > 0 && !insuranceCompany) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: `Укажите страховую компанию ${policyType}`,
|
||||
path: ['insurance'],
|
||||
});
|
||||
}
|
||||
|
||||
if (insCost > 0 && !insTerm) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: `Укажите срок страхования ${policyType}`,
|
||||
path: ['insurance'],
|
||||
});
|
||||
}
|
||||
|
||||
if (insCost > 0 && !insured) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: `Укажите плательщика ${policyType}`,
|
||||
path: ['insurance'],
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
@ -405,4 +405,42 @@ export default function reactions({ store, apolloClient }: ProcessContext) {
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
reaction(
|
||||
() =>
|
||||
$calculation.$values.getValues([
|
||||
'leaseObjectType',
|
||||
'engineVolume',
|
||||
'engineType',
|
||||
'leaseObjectMotorPower',
|
||||
]),
|
||||
async ({ leaseObjectType: leaseObjectTypeId }) => {
|
||||
if (!leaseObjectTypeId) {
|
||||
$calculation.element('selectEngineType').unblock();
|
||||
$calculation.element('tbxEngineVolume').unblock();
|
||||
$calculation.element('tbxLeaseObjectMotorPower').unblock();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const {
|
||||
data: { evo_leasingobject_type },
|
||||
} = await apolloClient.query({
|
||||
query: CRMTypes.GetLeaseObjectTypeDocument,
|
||||
variables: {
|
||||
leaseObjectTypeId,
|
||||
},
|
||||
});
|
||||
|
||||
if (evo_leasingobject_type?.evo_id === '8') {
|
||||
$calculation.element('selectEngineType').resetValue().block();
|
||||
$calculation.element('tbxEngineVolume').resetValue().block();
|
||||
$calculation.element('tbxLeaseObjectMotorPower').resetValue().block();
|
||||
} else {
|
||||
$calculation.element('selectEngineType').unblock();
|
||||
$calculation.element('tbxEngineVolume').unblock();
|
||||
$calculation.element('tbxLeaseObjectMotorPower').unblock();
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@ -1,11 +1,15 @@
|
||||
/* eslint-disable @typescript-eslint/naming-convention */
|
||||
import * as CRMTypes from '@/graphql/crm.types';
|
||||
import { createValidationSchema } from '../validation';
|
||||
import type { Elements } from '@/Components/Calculation/config/map/values';
|
||||
import type { ProcessContext } from '@/process/types';
|
||||
import ValidationHelper from '@/stores/validation/helper';
|
||||
import { autorun, reaction } from 'mobx';
|
||||
import { uid } from 'radash';
|
||||
|
||||
export default function reactions({ store, apolloClient }: ProcessContext) {
|
||||
const { $calculation } = store;
|
||||
const key = uid(7);
|
||||
|
||||
export default function reactions(context: ProcessContext) {
|
||||
const { $calculation } = context.store;
|
||||
|
||||
/**
|
||||
* Если model содержит данные и по связи Модель-Комплектация в CRM у данной модели есть связанные активные записи Комплектаций,
|
||||
@ -14,16 +18,23 @@ export default function reactions({ store, apolloClient }: ProcessContext) {
|
||||
autorun(
|
||||
() => {
|
||||
const selectConfiguration = $calculation.element('selectConfiguration');
|
||||
selectConfiguration.validate({
|
||||
invalid: selectConfiguration.getOptions()?.length > 0 && !selectConfiguration.getValue(),
|
||||
message: 'Не заполнено поле',
|
||||
});
|
||||
if (selectConfiguration.getOptions()?.length > 0 && !selectConfiguration.getValue()) {
|
||||
selectConfiguration.setError({
|
||||
key,
|
||||
message: 'Не заполнено поле',
|
||||
});
|
||||
} else {
|
||||
selectConfiguration.removeError({ key });
|
||||
}
|
||||
},
|
||||
{
|
||||
delay: 10,
|
||||
}
|
||||
);
|
||||
|
||||
const validationSchema = createValidationSchema(context);
|
||||
const helper = new ValidationHelper();
|
||||
|
||||
reaction(
|
||||
() =>
|
||||
$calculation.$values.getValues([
|
||||
@ -31,134 +42,25 @@ export default function reactions({ store, apolloClient }: ProcessContext) {
|
||||
'engineVolume',
|
||||
'engineType',
|
||||
'leaseObjectMotorPower',
|
||||
'countSeats',
|
||||
'maxMass',
|
||||
'leaseObjectCategory',
|
||||
]),
|
||||
async ({
|
||||
engineType,
|
||||
engineVolume,
|
||||
leaseObjectType: leaseObjectTypeId,
|
||||
leaseObjectMotorPower,
|
||||
}) => {
|
||||
if (!leaseObjectTypeId) {
|
||||
$calculation.element('selectEngineType').unblock();
|
||||
$calculation.element('tbxEngineVolume').unblock();
|
||||
$calculation.element('tbxLeaseObjectMotorPower').unblock();
|
||||
async (values) => {
|
||||
helper.removeErrors();
|
||||
const validationResult = await validationSchema.safeParseAsync(values);
|
||||
|
||||
return;
|
||||
if (!validationResult.success) {
|
||||
validationResult.error.errors.forEach(({ path, message }) => {
|
||||
(path as Elements[]).forEach((elementName) => {
|
||||
const removeError = $calculation.element(elementName).setError({ key, message });
|
||||
if (removeError) helper.add(removeError);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
const {
|
||||
data: { evo_leasingobject_type },
|
||||
} = await apolloClient.query({
|
||||
query: CRMTypes.GetLeaseObjectTypeDocument,
|
||||
variables: {
|
||||
leaseObjectTypeId,
|
||||
},
|
||||
});
|
||||
|
||||
if (evo_leasingobject_type?.evo_id === '8') {
|
||||
$calculation.element('selectEngineType').resetValue().block();
|
||||
$calculation.element('tbxEngineVolume').resetValue().block();
|
||||
$calculation.element('tbxLeaseObjectMotorPower').resetValue().block();
|
||||
} else {
|
||||
$calculation.element('selectEngineType').unblock();
|
||||
$calculation.element('tbxEngineVolume').unblock();
|
||||
$calculation.element('tbxLeaseObjectMotorPower').unblock();
|
||||
}
|
||||
|
||||
const isNotTrailer =
|
||||
evo_leasingobject_type?.evo_id !== null && evo_leasingobject_type?.evo_id !== '8';
|
||||
|
||||
$calculation.element('tbxEngineVolume').validate({
|
||||
invalid: isNotTrailer && engineVolume <= 0,
|
||||
message: 'Не заполнено поле',
|
||||
});
|
||||
$calculation.element('selectEngineType').validate({
|
||||
invalid: isNotTrailer && !engineType,
|
||||
message: 'Не заполнено поле',
|
||||
});
|
||||
$calculation.element('tbxLeaseObjectMotorPower').validate({
|
||||
invalid: isNotTrailer && leaseObjectMotorPower <= 0,
|
||||
message: 'Не заполнено поле',
|
||||
});
|
||||
},
|
||||
{
|
||||
delay: 100,
|
||||
}
|
||||
);
|
||||
|
||||
{
|
||||
const validationHelper = new ValidationHelper();
|
||||
|
||||
reaction(
|
||||
() => $calculation.$values.getValues(['leaseObjectType', 'countSeats', 'maxMass']),
|
||||
async ({ countSeats, leaseObjectType: leaseObjectTypeId, maxMass }) => {
|
||||
if (!leaseObjectTypeId) {
|
||||
validationHelper.removeErrors();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const {
|
||||
data: { evo_leasingobject_type },
|
||||
} = await apolloClient.query({
|
||||
query: CRMTypes.GetLeaseObjectTypeDocument,
|
||||
variables: {
|
||||
leaseObjectTypeId,
|
||||
},
|
||||
});
|
||||
|
||||
$calculation.element('tbxCountSeats').validate({
|
||||
helper: validationHelper,
|
||||
invalid: evo_leasingobject_type?.evo_id === '1' && countSeats >= 9,
|
||||
message: 'Количество мест должно быть меньше 9',
|
||||
});
|
||||
|
||||
$calculation.element('tbxCountSeats').validate({
|
||||
helper: validationHelper,
|
||||
invalid:
|
||||
(evo_leasingobject_type?.evo_id === '4' || evo_leasingobject_type?.evo_id === '5') &&
|
||||
countSeats <= 8,
|
||||
message: 'Количество мест должно быть больше 8',
|
||||
});
|
||||
|
||||
$calculation.element('tbxMaxMass').validate({
|
||||
helper: validationHelper,
|
||||
invalid: evo_leasingobject_type?.evo_id === '2' && maxMass <= 0,
|
||||
message: 'Не заполнено поле',
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
{
|
||||
const validationHelper = new ValidationHelper();
|
||||
|
||||
reaction(
|
||||
() => $calculation.$values.getValues(['leaseObjectType', 'leaseObjectCategory']),
|
||||
async ({ leaseObjectCategory, leaseObjectType: leaseObjectTypeId }) => {
|
||||
if (!leaseObjectTypeId) {
|
||||
validationHelper.removeErrors();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const {
|
||||
data: { evo_leasingobject_type },
|
||||
} = await apolloClient.query({
|
||||
query: CRMTypes.GetLeaseObjectTypeDocument,
|
||||
variables: {
|
||||
leaseObjectTypeId,
|
||||
},
|
||||
});
|
||||
|
||||
$calculation.element('selectLeaseObjectCategory').validate({
|
||||
helper: validationHelper,
|
||||
invalid:
|
||||
!leaseObjectCategory &&
|
||||
Boolean(
|
||||
evo_leasingobject_type?.evo_id &&
|
||||
!['6', '9', '10'].includes(evo_leasingobject_type?.evo_id)
|
||||
),
|
||||
message: 'Не заполнено поле',
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
109
apps/web/process/leasing-object/validation.ts
Normal file
109
apps/web/process/leasing-object/validation.ts
Normal file
@ -0,0 +1,109 @@
|
||||
/* eslint-disable @typescript-eslint/naming-convention */
|
||||
import type { ValidationContext } from '../types';
|
||||
import ValuesSchema from '@/config/schema/values';
|
||||
import * as CRMTypes from '@/graphql/crm.types';
|
||||
import { z } from 'zod';
|
||||
|
||||
export function createValidationSchema({ apolloClient }: ValidationContext) {
|
||||
return ValuesSchema.pick({
|
||||
countSeats: true,
|
||||
engineType: true,
|
||||
engineVolume: true,
|
||||
leaseObjectCategory: true,
|
||||
leaseObjectMotorPower: true,
|
||||
leaseObjectType: true,
|
||||
maxMass: true,
|
||||
}).superRefine(
|
||||
async (
|
||||
{
|
||||
leaseObjectType: leaseObjectTypeId,
|
||||
engineVolume,
|
||||
engineType,
|
||||
leaseObjectMotorPower,
|
||||
countSeats,
|
||||
maxMass,
|
||||
leaseObjectCategory,
|
||||
},
|
||||
ctx
|
||||
) => {
|
||||
if (leaseObjectTypeId) {
|
||||
const {
|
||||
data: { evo_leasingobject_type },
|
||||
} = await apolloClient.query({
|
||||
query: CRMTypes.GetLeaseObjectTypeDocument,
|
||||
variables: {
|
||||
leaseObjectTypeId,
|
||||
},
|
||||
});
|
||||
|
||||
const isNotTrailer =
|
||||
evo_leasingobject_type?.evo_id !== null && evo_leasingobject_type?.evo_id !== '8';
|
||||
|
||||
if (isNotTrailer && engineVolume <= 0) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: 'Не заполнено поле',
|
||||
path: ['tbxEngineVolume'],
|
||||
});
|
||||
}
|
||||
|
||||
if (isNotTrailer && !engineType) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: 'Не заполнено поле',
|
||||
path: ['selectEngineType'],
|
||||
});
|
||||
}
|
||||
|
||||
if (isNotTrailer && leaseObjectMotorPower <= 0) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: 'Не заполнено поле',
|
||||
path: ['tbxLeaseObjectMotorPower'],
|
||||
});
|
||||
}
|
||||
|
||||
if (evo_leasingobject_type?.evo_id === '1' && countSeats >= 9) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: 'Количество мест должно быть меньше 9',
|
||||
path: ['tbxCountSeats'],
|
||||
});
|
||||
}
|
||||
|
||||
if (
|
||||
(evo_leasingobject_type?.evo_id === '4' || evo_leasingobject_type?.evo_id === '5') &&
|
||||
countSeats <= 8
|
||||
) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: 'Количество мест должно быть больше 8',
|
||||
path: ['tbxCountSeats'],
|
||||
});
|
||||
}
|
||||
|
||||
if (evo_leasingobject_type?.evo_id === '2' && maxMass <= 0) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: 'Не заполнено поле',
|
||||
path: ['tbxMaxMass'],
|
||||
});
|
||||
}
|
||||
|
||||
if (
|
||||
!leaseObjectCategory &&
|
||||
Boolean(
|
||||
evo_leasingobject_type?.evo_id &&
|
||||
!['6', '9', '10'].includes(evo_leasingobject_type?.evo_id)
|
||||
)
|
||||
) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: 'Не заполнено поле',
|
||||
path: ['selectLeaseObjectCategory'],
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
@ -1,138 +0,0 @@
|
||||
import { SEASONS_PERIOD_NUMBER, SEASONS_PERIODS } from './seasons-constants';
|
||||
import { MIN_PAYMENT } from '@/constants/values';
|
||||
import type RootStore from '@/stores/root';
|
||||
import { counting, max, min, shift, sort } from 'radash';
|
||||
import { areEqual, isSorted } from 'tools/array';
|
||||
|
||||
// eslint-disable-next-line sonarjs/cognitive-complexity
|
||||
export default function validatePaymentsTable({ $calculation, $tables }: RootStore) {
|
||||
/**
|
||||
* в таблице платежей в столбце Соотношение платежей
|
||||
* для строк с 2 до "Срок лизинга-1" минимальное значение должно быть равно 3
|
||||
*/
|
||||
{
|
||||
const leasingPeriod = $calculation.element('tbxLeasingPeriod').getValue();
|
||||
const targetPayments = $tables.payments.values.slice(1, leasingPeriod - 1);
|
||||
|
||||
if (!targetPayments.every((payment) => payment >= MIN_PAYMENT)) {
|
||||
return `Минимальное значение платежа должно быть равно ${MIN_PAYMENT}`;
|
||||
}
|
||||
}
|
||||
|
||||
switch ($calculation.element('radioGraphType').getValue()) {
|
||||
// Дегрессия
|
||||
case 100_000_001: {
|
||||
if (!$calculation.element('selectSeasonType').getValue()) {
|
||||
return 'Не выбран тип дегрессии';
|
||||
}
|
||||
|
||||
/**
|
||||
* при Дегрессии все значения не должны быть равны друг другу
|
||||
* + что при Легком старте 2,3 и 4 платежи не должны быть равны 100
|
||||
*/
|
||||
{
|
||||
const leasingPeriod = $calculation.element('tbxLeasingPeriod').getValue();
|
||||
const targetPayments = $tables.payments.values.slice(1, leasingPeriod - 1);
|
||||
|
||||
if (new Set(targetPayments).size === 1) {
|
||||
return 'Платежи не должны быть равны друг другу';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Проверка на возрастание
|
||||
*/
|
||||
{
|
||||
const leasingPeriod = $calculation.element('tbxLeasingPeriod').getValue();
|
||||
const targetPayments = $tables.payments.values.slice(1, leasingPeriod - 1);
|
||||
|
||||
for (let i = 2; i < targetPayments.length - 1; i += 1) {
|
||||
if (targetPayments[i] > targetPayments[i - 1]) {
|
||||
return 'Платежи должны убывать';
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Если вид графика = Дегрессия И значения в "Соотношении платежей" у 2, 3 и 4 платежа отличаются друг от друга не более чем на 10
|
||||
* (т.е. берем значения в этих полях, определяем максимальное и минимальное значение и смотрим на их разницу)
|
||||
* то не осуществлять Расчет графика и выводить ошибку "Нельзя осуществить расчет - указана очень жетская дегрессия.
|
||||
* На 2-4 платежах Соотношение платежа должен отличаться не более чем на 10%",
|
||||
* иначе осуществлять расчет
|
||||
*/
|
||||
{
|
||||
const targetPayments = $tables.payments.values.slice(1, 4);
|
||||
|
||||
if ((max(targetPayments) || 0) - (min(targetPayments) || 0) > 10) {
|
||||
return 'Указана очень жесткая дегрессия. На 2-4 платежах Соотношение платежа должен отличаться не более чем на 10%';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Если вид графика = Дегрессия И значения в "Соотношении платежей" для строк с 2 До "Срок лизинга-1" как минимум 2 раза по 2 платежа должны между собой быть равны
|
||||
* (т.е. берем значения "Соотношения платежей" для строк с 2 до "Срок лизинга-1" и делаем сводную таблицу - если кол-во одинаковых значение больше 2 встречаются 2 и более раза),
|
||||
* то осуществлять расчет,
|
||||
* иначе не осуществлять Расчет графика и выводить ошибку "Нельзя осуществить расчет - указана очень жетская дегрессия. Не менее чем у 4х платежей "Соотношение платежа" должно не отличаться между самой",
|
||||
*/
|
||||
{
|
||||
const leasingPeriod = $calculation.element('tbxLeasingPeriod').getValue();
|
||||
const targetPayments = $tables.payments.values.slice(1, leasingPeriod - 1);
|
||||
const counts = counting(targetPayments, (v) => v);
|
||||
if (Object.values(counts).filter((count) => count > 1).length < 2) {
|
||||
return 'Указана очень жесткая дегрессия. Не менее чем у 4х платежей соотношение должно не отличаться между собой';
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case 100_000_003: {
|
||||
const leasingPeriod = $calculation.element('tbxLeasingPeriod').getValue();
|
||||
if (leasingPeriod < 14) {
|
||||
return 'При сезонном виде графика срок лизинга должен быть больше 14 месяцев';
|
||||
}
|
||||
|
||||
const seasonType = $calculation.element('selectSeasonType').getValue();
|
||||
if (!seasonType) {
|
||||
return 'Не выбран тип сезонности';
|
||||
}
|
||||
|
||||
const highSeasonStartOption = $calculation.element('selectHighSeasonStart').getOption();
|
||||
if (!highSeasonStartOption) {
|
||||
return 'Не выбрано смещение сезонности';
|
||||
}
|
||||
|
||||
{
|
||||
const seasons = $tables.payments.values.slice(1, SEASONS_PERIOD_NUMBER + 1);
|
||||
const shiftNumber = Number.parseInt(highSeasonStartOption.label, 10) - 2;
|
||||
const unshiftedSeasons = shift(seasons, -shiftNumber);
|
||||
|
||||
const positions = SEASONS_PERIODS[seasonType];
|
||||
const seasonsValues = positions.map((position) => unshiftedSeasons[position]);
|
||||
|
||||
if (isSorted(seasonsValues)) {
|
||||
return 'Сезонные платежи должны убывать';
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// Легкий старт
|
||||
case 100_000_004: {
|
||||
const targetPayments = $tables.payments.values.slice(1, 4);
|
||||
const sortedPayments = sort(targetPayments, (x) => x);
|
||||
const areEqualPayments = new Set(targetPayments).size === 1;
|
||||
|
||||
if (!areEqual(targetPayments, sortedPayments) || areEqualPayments) {
|
||||
return '2, 3, 4 платежи должны возрастать';
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
@ -1,64 +1,56 @@
|
||||
import validatePaymentsTable from '../lib/validation';
|
||||
import { MIN_LASTPAYMENT_NSIB } from '@/constants/values';
|
||||
import { createValidationSchema } from '../validation';
|
||||
import type { Elements } from '@/Components/Calculation/config/map/values';
|
||||
import type { ProcessContext } from '@/process/types';
|
||||
import ValidationHelper from '@/stores/validation/helper';
|
||||
import { comparer, reaction, toJS } from 'mobx';
|
||||
import { uid } from 'radash';
|
||||
|
||||
export default function reactions({ store }: ProcessContext) {
|
||||
const { $calculation, $tables } = store;
|
||||
const key = uid(7);
|
||||
|
||||
const validationHelper = new ValidationHelper();
|
||||
export default function reactions(context: ProcessContext) {
|
||||
const { $calculation, $tables } = context.store;
|
||||
|
||||
const validationSchema = createValidationSchema();
|
||||
const helper = 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();
|
||||
const values = $calculation.$values.getValues([
|
||||
'graphType',
|
||||
'highSeasonStart',
|
||||
'leasingPeriod',
|
||||
'seasonType',
|
||||
'insNSIB',
|
||||
'lastPaymentRub',
|
||||
]);
|
||||
|
||||
return {
|
||||
graphType,
|
||||
highSeasonStart,
|
||||
leasingPeriod,
|
||||
payments,
|
||||
seasonType,
|
||||
payments: { values: payments },
|
||||
...values,
|
||||
};
|
||||
},
|
||||
() => {
|
||||
validationHelper.removeErrors();
|
||||
async (values) => {
|
||||
helper.removeErrors();
|
||||
const validationResult = await validationSchema.safeParseAsync(values);
|
||||
|
||||
const errorText = validatePaymentsTable(store);
|
||||
|
||||
if (errorText) {
|
||||
$tables.payments.validate({
|
||||
helper: validationHelper,
|
||||
invalid: errorText !== null,
|
||||
message: errorText,
|
||||
if (!validationResult.success) {
|
||||
validationResult.error.errors.forEach(({ path, message }) => {
|
||||
(path as Array<Elements & 'payments'>).forEach((elementName) => {
|
||||
if (elementName === 'payments') {
|
||||
const removeError = $tables.payments.setError({ key, message });
|
||||
if (removeError) helper.add(removeError);
|
||||
} else {
|
||||
const removeError = $calculation.element(elementName).setError({ key, message });
|
||||
if (removeError) helper.add(removeError);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
},
|
||||
{
|
||||
delay: 50,
|
||||
delay: 100,
|
||||
equals: comparer.structural,
|
||||
}
|
||||
);
|
||||
|
||||
reaction(
|
||||
() => {
|
||||
const lastPaymentRub = $calculation.element('tbxLastPaymentRub').getValue();
|
||||
const insNSIB = $calculation.element('selectInsNSIB').getValue();
|
||||
|
||||
return {
|
||||
insNSIB,
|
||||
lastPaymentRub,
|
||||
};
|
||||
},
|
||||
({ lastPaymentRub, insNSIB }) => {
|
||||
$calculation.element('tbxLastPaymentRub').validate({
|
||||
invalid: Boolean(insNSIB) && lastPaymentRub < MIN_LASTPAYMENT_NSIB,
|
||||
message: `Последний платеж меньше ${MIN_LASTPAYMENT_NSIB} руб. не может быть при наличии НСИБ, укажите большее значение`,
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
215
apps/web/process/payments/validation.ts
Normal file
215
apps/web/process/payments/validation.ts
Normal file
@ -0,0 +1,215 @@
|
||||
/* eslint-disable sonarjs/cognitive-complexity */
|
||||
/* eslint-disable complexity */
|
||||
import { SEASONS_PERIOD_NUMBER, SEASONS_PERIODS } from './lib/seasons-constants';
|
||||
import { selectHighSeasonStart } from '@/config/default-options';
|
||||
import PaymentsSchema from '@/config/schema/payments';
|
||||
import ValuesSchema from '@/config/schema/values';
|
||||
import { MIN_LASTPAYMENT_NSIB, MIN_PAYMENT } from '@/constants/values';
|
||||
import { counting, max, min, shift, sort } from 'radash';
|
||||
import { areEqual, isSorted } from 'tools/array';
|
||||
import { z } from 'zod';
|
||||
|
||||
export function createValidationSchema() {
|
||||
return ValuesSchema.pick({
|
||||
graphType: true,
|
||||
highSeasonStart: true,
|
||||
insNSIB: true,
|
||||
lastPaymentRub: true,
|
||||
leasingPeriod: true,
|
||||
seasonType: true,
|
||||
})
|
||||
.extend({
|
||||
payments: PaymentsSchema,
|
||||
})
|
||||
.superRefine(
|
||||
async (
|
||||
{
|
||||
graphType,
|
||||
highSeasonStart,
|
||||
leasingPeriod,
|
||||
payments,
|
||||
seasonType,
|
||||
insNSIB,
|
||||
lastPaymentRub,
|
||||
},
|
||||
ctx
|
||||
) => {
|
||||
if (Boolean(insNSIB) && lastPaymentRub < MIN_LASTPAYMENT_NSIB) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: `Последний платеж не может быть меньше ${MIN_LASTPAYMENT_NSIB} руб. при наличии НСИБ, укажите большее значение`,
|
||||
path: ['tbxLastPaymentRub'],
|
||||
});
|
||||
}
|
||||
|
||||
{
|
||||
const targetPayments = payments.values.slice(1, leasingPeriod - 1);
|
||||
|
||||
if (!targetPayments.every((payment) => payment >= MIN_PAYMENT)) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: `Минимальное значение платежа должно быть равно ${MIN_PAYMENT}`,
|
||||
path: ['payments'],
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
switch (graphType) {
|
||||
// Дегрессия
|
||||
case 100_000_001: {
|
||||
if (!seasonType) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: 'Не выбран тип дегрессии',
|
||||
path: ['payments', 'selectSeasonType'],
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
/**
|
||||
* при Дегрессии все значения не должны быть равны друг другу
|
||||
* + что при Легком старте 2,3 и 4 платежи не должны быть равны 100
|
||||
*/
|
||||
{
|
||||
const targetPayments = payments.values.slice(1, leasingPeriod - 1);
|
||||
if (new Set(targetPayments).size === 1) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: 'Платежи не должны быть равны друг другу',
|
||||
path: ['payments'],
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Проверка на возрастание
|
||||
*/
|
||||
{
|
||||
const targetPayments = payments.values.slice(1, leasingPeriod - 1);
|
||||
for (let i = 2; i < targetPayments.length - 1; i += 1) {
|
||||
if (targetPayments[i] > targetPayments[i - 1]) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: 'Платежи должны убывать',
|
||||
path: ['payments'],
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Если вид графика = Дегрессия И значения в "Соотношении платежей" у 2, 3 и 4 платежа отличаются друг от друга не более чем на 10
|
||||
* (т.е. берем значения в этих полях, определяем максимальное и минимальное значение и смотрим на их разницу)
|
||||
* то не осуществлять Расчет графика и выводить ошибку "Нельзя осуществить расчет - указана очень жетская дегрессия.
|
||||
* На 2-4 платежах Соотношение платежа должен отличаться не более чем на 10%",
|
||||
* иначе осуществлять расчет
|
||||
*/
|
||||
{
|
||||
const targetPayments = payments.values.slice(1, 4);
|
||||
|
||||
if ((max(targetPayments) || 0) - (min(targetPayments) || 0) > 10) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message:
|
||||
'Указана очень жесткая дегрессия. На 2-4 платежах Соотношение платежа должен отличаться не более чем на 10%',
|
||||
path: ['payments'],
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Если вид графика = Дегрессия И значения в "Соотношении платежей" для строк с 2 До "Срок лизинга-1" как минимум 2 раза по 2 платежа должны между собой быть равны
|
||||
* (т.е. берем значения "Соотношения платежей" для строк с 2 до "Срок лизинга-1" и делаем сводную таблицу - если кол-во одинаковых значение больше 2 встречаются 2 и более раза),
|
||||
* то осуществлять расчет,
|
||||
* иначе не осуществлять Расчет графика и выводить ошибку "Нельзя осуществить расчет - указана очень жетская дегрессия. Не менее чем у 4х платежей "Соотношение платежа" должно не отличаться между самой",
|
||||
*/
|
||||
{
|
||||
const targetPayments = payments.values.slice(1, leasingPeriod - 1);
|
||||
const counts = counting(targetPayments, (v) => v);
|
||||
if (Object.values(counts).filter((count) => count > 1).length < 2) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message:
|
||||
'Указана очень жесткая дегрессия. Не менее чем у 4х платежей соотношение должно не отличаться между собой',
|
||||
path: ['payments'],
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case 100_000_003: {
|
||||
if (leasingPeriod < 14) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: 'При сезонном виде графика срок лизинга должен быть больше 14 месяцев',
|
||||
path: ['payments', 'tbxLeasingPeriod'],
|
||||
});
|
||||
}
|
||||
|
||||
if (!seasonType) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: 'Не выбран тип сезонности',
|
||||
path: ['payments', 'selectSeasonType'],
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
if (!highSeasonStart) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: 'Не выбрано смещение сезонности',
|
||||
path: ['payments', 'selectHighSeasonStart'],
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
const seasons = payments.values.slice(1, SEASONS_PERIOD_NUMBER + 1);
|
||||
const highSeasonStartOption = selectHighSeasonStart.find(
|
||||
(x) => x.value === highSeasonStart
|
||||
);
|
||||
|
||||
if (highSeasonStartOption) {
|
||||
const shiftNumber = Number.parseInt(highSeasonStartOption.label, 10) - 2;
|
||||
const unshiftedSeasons = shift(seasons, -shiftNumber);
|
||||
|
||||
const positions = SEASONS_PERIODS[seasonType];
|
||||
const seasonsValues = positions.map((position) => unshiftedSeasons[position]);
|
||||
|
||||
if (isSorted(seasonsValues)) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: 'Сезонные платежи должны убывать',
|
||||
path: ['payments'],
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// Легкий старт
|
||||
case 100_000_004: {
|
||||
const targetPayments = payments.values.slice(1, 4);
|
||||
const sortedPayments = sort(targetPayments, (x) => x);
|
||||
const areEqualPayments = new Set(targetPayments).size === 1;
|
||||
|
||||
if (!areEqual(targetPayments, sortedPayments) || areEqualPayments) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: '2, 3, 4 платежи должны возрастать',
|
||||
path: ['payments'],
|
||||
});
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
@ -1,74 +1,45 @@
|
||||
/* eslint-disable @typescript-eslint/naming-convention */
|
||||
import { VAT } from '@/constants/values';
|
||||
import * as CRMTypes from '@/graphql/crm.types';
|
||||
import { createValidationSchema } from '../validation';
|
||||
import type { Elements } from '@/Components/Calculation/config/map/values';
|
||||
import type { ProcessContext } from '@/process/types';
|
||||
import ValidationHelper from '@/stores/validation/helper';
|
||||
import { reaction } from 'mobx';
|
||||
import { round } from 'tools';
|
||||
import { uid } from 'radash';
|
||||
|
||||
export default function reactions({ store, apolloClient }: ProcessContext) {
|
||||
const key = uid(7);
|
||||
|
||||
export default function reactions(context: ProcessContext) {
|
||||
const { store } = context;
|
||||
const { $calculation } = store;
|
||||
const validationSchema = createValidationSchema(context);
|
||||
|
||||
const helper = new ValidationHelper();
|
||||
reaction(
|
||||
() =>
|
||||
$calculation.$values.getValues([
|
||||
'VATInLeaseObjectPrice',
|
||||
'leaseObjectPriceWthtVAT',
|
||||
'product',
|
||||
'supplierDiscountRub',
|
||||
'plPriceRub',
|
||||
'firstPaymentRub',
|
||||
'subsidySum',
|
||||
]),
|
||||
async ({ VATInLeaseObjectPrice, leaseObjectPriceWthtVAT, product: productId }) => {
|
||||
let evo_sale_without_nds = false;
|
||||
async (values) => {
|
||||
helper.removeErrors();
|
||||
const validationResult = await validationSchema.safeParseAsync(values);
|
||||
|
||||
if (productId) {
|
||||
const {
|
||||
data: { evo_baseproduct },
|
||||
} = await apolloClient.query({
|
||||
query: CRMTypes.GetProductDocument,
|
||||
variables: {
|
||||
productId,
|
||||
},
|
||||
if (!validationResult.success) {
|
||||
validationResult.error.errors.forEach(({ path, message }) => {
|
||||
(path as Elements[]).forEach((elementName) => {
|
||||
const removeError = $calculation.element(elementName).setError({ key, message });
|
||||
if (removeError) helper.add(removeError);
|
||||
});
|
||||
});
|
||||
if (evo_baseproduct?.evo_sale_without_nds) {
|
||||
evo_sale_without_nds = evo_baseproduct.evo_sale_without_nds;
|
||||
}
|
||||
}
|
||||
|
||||
$calculation.element('tbxVATInLeaseObjectPrice').validate({
|
||||
invalid:
|
||||
evo_sale_without_nds && round(VATInLeaseObjectPrice / leaseObjectPriceWthtVAT, 2) >= VAT,
|
||||
message:
|
||||
'При продаже ПЛ после ФЛ размер НДС в стоимости ПЛ не может составлять 20% и более от стоимости с НДС. Проверьте корректность НДС, либо измените Продукт',
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
reaction(
|
||||
() => $calculation.$values.getValues(['supplierDiscountRub', 'plPriceRub']),
|
||||
({ supplierDiscountRub, plPriceRub }) => {
|
||||
$calculation.element('tbxSupplierDiscountRub').validate({
|
||||
invalid: supplierDiscountRub >= plPriceRub,
|
||||
message: 'Скидка от поставщика не может быть больше или равна стоимости ПЛ',
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
reaction(
|
||||
() => $calculation.$values.getValues(['firstPaymentRub', 'plPriceRub']),
|
||||
({ firstPaymentRub, plPriceRub }) => {
|
||||
$calculation.element('tbxFirstPaymentRub').validate({
|
||||
invalid: firstPaymentRub >= plPriceRub,
|
||||
message: 'Первый платеж не может быть больше или равен стоимости ПЛ',
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
reaction(
|
||||
() => $calculation.$values.getValues(['firstPaymentRub', 'subsidySum']),
|
||||
({ firstPaymentRub, subsidySum }) => {
|
||||
$calculation.element('tbxFirstPaymentRub').validate({
|
||||
invalid: firstPaymentRub - subsidySum < 0,
|
||||
message:
|
||||
'Первый платеж с учетом субсидии получается отрицательный, увеличьте первый платеж',
|
||||
});
|
||||
},
|
||||
{
|
||||
delay: 100,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
80
apps/web/process/price/validation.ts
Normal file
80
apps/web/process/price/validation.ts
Normal file
@ -0,0 +1,80 @@
|
||||
/* eslint-disable @typescript-eslint/naming-convention */
|
||||
import type { ValidationContext } from '../types';
|
||||
import ValuesSchema from '@/config/schema/values';
|
||||
import { VAT } from '@/constants/values';
|
||||
import * as CRMTypes from '@/graphql/crm.types';
|
||||
import { round } from 'tools';
|
||||
import { z } from 'zod';
|
||||
|
||||
export function createValidationSchema({ apolloClient }: ValidationContext) {
|
||||
return ValuesSchema.pick({
|
||||
VATInLeaseObjectPrice: true,
|
||||
firstPaymentRub: true,
|
||||
leaseObjectPriceWthtVAT: true,
|
||||
plPriceRub: true,
|
||||
product: true,
|
||||
subsidySum: true,
|
||||
supplierDiscountRub: true,
|
||||
}).superRefine(
|
||||
async (
|
||||
{
|
||||
VATInLeaseObjectPrice,
|
||||
leaseObjectPriceWthtVAT,
|
||||
product: productId,
|
||||
supplierDiscountRub,
|
||||
plPriceRub,
|
||||
firstPaymentRub,
|
||||
subsidySum,
|
||||
},
|
||||
ctx
|
||||
) => {
|
||||
if (productId) {
|
||||
const {
|
||||
data: { evo_baseproduct },
|
||||
} = await apolloClient.query({
|
||||
query: CRMTypes.GetProductDocument,
|
||||
variables: {
|
||||
productId,
|
||||
},
|
||||
});
|
||||
|
||||
if (
|
||||
evo_baseproduct?.evo_sale_without_nds &&
|
||||
round(VATInLeaseObjectPrice / leaseObjectPriceWthtVAT, 2) >= VAT
|
||||
) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message:
|
||||
'При продаже ПЛ после ФЛ размер НДС в стоимости ПЛ не может составлять 20% и более от стоимости с НДС. Проверьте корректность НДС, либо измените Продукт',
|
||||
path: ['tbxVATInLeaseObjectPrice'],
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (supplierDiscountRub >= plPriceRub) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: 'Скидка не может быть больше или равна стоимости ПЛ',
|
||||
path: ['tbxSupplierDiscountRub'],
|
||||
});
|
||||
}
|
||||
|
||||
if (firstPaymentRub >= plPriceRub) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: 'Первый платеж не может быть больше или равен стоимости ПЛ',
|
||||
path: ['tbxFirstPaymentRub'],
|
||||
});
|
||||
}
|
||||
|
||||
if (firstPaymentRub - subsidySum < 0) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message:
|
||||
'Первый платеж с учетом субсидии получается отрицательный, увеличьте первый платеж',
|
||||
path: ['tbxFirstPaymentRub'],
|
||||
});
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
@ -2,11 +2,10 @@
|
||||
import type { AgentsFields, AgentsRewardConditionsFields, AgentsSumFields } from './types';
|
||||
import * as CRMTypes from '@/graphql/crm.types';
|
||||
import type RootStore from '@/stores/root';
|
||||
import ValidationHelper from '@/stores/validation/helper';
|
||||
import type { ApolloClient } from '@apollo/client';
|
||||
import dayjs from 'dayjs';
|
||||
import utc from 'dayjs/plugin/utc';
|
||||
import { autorun, reaction } from 'mobx';
|
||||
import { reaction } from 'mobx';
|
||||
import { normalizeOptions } from 'tools/entity';
|
||||
import { makeDisposable } from 'tools/mobx';
|
||||
|
||||
@ -104,108 +103,3 @@ export function fillAgentRewardSummReaction(
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export function validateAgentRewardSumm(
|
||||
store: RootStore,
|
||||
apolloClient: ApolloClient<object>,
|
||||
agentParams: {
|
||||
rewardConditionField: AgentsRewardConditionsFields;
|
||||
rewardSummField: AgentsSumFields;
|
||||
}
|
||||
) {
|
||||
const { $calculation } = store;
|
||||
const { rewardConditionField, rewardSummField } = agentParams;
|
||||
|
||||
const validationHelper = new ValidationHelper();
|
||||
|
||||
reaction(
|
||||
() => $calculation.element(rewardSummField).getValue(),
|
||||
async (rewardSumm) => {
|
||||
const conditionId = $calculation.element(rewardConditionField).getValue();
|
||||
if (!conditionId) {
|
||||
validationHelper.removeErrors();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const {
|
||||
data: { evo_reward_condition },
|
||||
} = await apolloClient.query<
|
||||
CRMTypes.GetRewardConditionQuery,
|
||||
CRMTypes.GetRewardConditionQueryVariables
|
||||
>({
|
||||
query: CRMTypes.GetRewardConditionDocument,
|
||||
variables: {
|
||||
conditionId,
|
||||
},
|
||||
});
|
||||
|
||||
if (!evo_reward_condition) {
|
||||
validationHelper.removeErrors();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (evo_reward_condition.evo_reward_summ) {
|
||||
$calculation.element(rewardSummField).validate({
|
||||
helper: validationHelper,
|
||||
invalid: rewardSumm > evo_reward_condition?.evo_reward_summ,
|
||||
message: 'Вознаграждение указано больше условия по агентскому договору!',
|
||||
});
|
||||
}
|
||||
|
||||
if (
|
||||
evo_reward_condition?.evo_reduce_reward !== null &&
|
||||
evo_reward_condition.evo_reward_summ
|
||||
) {
|
||||
$calculation.element(rewardSummField).validate({
|
||||
helper: validationHelper,
|
||||
invalid:
|
||||
evo_reward_condition.evo_reduce_reward === false &&
|
||||
rewardSumm < evo_reward_condition.evo_reward_summ,
|
||||
message: 'Вознаграждение указано меньше условия по агентскому договору!',
|
||||
});
|
||||
}
|
||||
|
||||
if (evo_reward_condition?.evo_min_reward_summ !== null) {
|
||||
$calculation.element(rewardSummField).validate({
|
||||
helper: validationHelper,
|
||||
invalid: rewardSumm < evo_reward_condition?.evo_min_reward_summ,
|
||||
message: 'Вознаграждение указано меньше условия по агентскому договору!',
|
||||
});
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
autorun(() => {
|
||||
const rewardSumm = $calculation.element(rewardSummField).getValue();
|
||||
const rewardConditionOptions = $calculation.$options.getOptions(rewardConditionField);
|
||||
|
||||
const conditionIds = rewardConditionOptions.map((x) => x.value);
|
||||
const requests = conditionIds.map(async (conditionId) => {
|
||||
const {
|
||||
data: { evo_reward_condition },
|
||||
} = await apolloClient.query<
|
||||
CRMTypes.GetRewardConditionQuery,
|
||||
CRMTypes.GetRewardConditionQueryVariables
|
||||
>({
|
||||
query: CRMTypes.GetRewardConditionDocument,
|
||||
variables: {
|
||||
conditionId,
|
||||
},
|
||||
});
|
||||
|
||||
return evo_reward_condition;
|
||||
});
|
||||
|
||||
Promise.all(requests).then((results) => {
|
||||
const required_reward = results.some(
|
||||
(x) => x?.evo_agency_agreementidData?.evo_required_reward === true
|
||||
);
|
||||
$calculation.element(rewardSummField).validate({
|
||||
invalid: rewardSumm === 0 && required_reward,
|
||||
message: 'Согласно агентскому договору обязательна выплата АВ. Заложите АВ в расчет',
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@ -6,12 +6,11 @@ import * as CRMTypes from '@/graphql/crm.types';
|
||||
import type { ProcessContext } from '@/process/types';
|
||||
import dayjs from 'dayjs';
|
||||
import utc from 'dayjs/plugin/utc';
|
||||
import { autorun, reaction } from 'mobx';
|
||||
import { reaction } from 'mobx';
|
||||
import { makeDisposable } from 'tools/mobx';
|
||||
|
||||
const { fillIndAgent, fillCalcBroker, fillCalcDoubleAgent, fillFinDepartment } = fillAgentsFromLead;
|
||||
const { fillAgentRewardReaction, fillAgentRewardSummReaction, validateAgentRewardSumm } =
|
||||
createReactions;
|
||||
const { fillAgentRewardReaction, fillAgentRewardSummReaction } = createReactions;
|
||||
|
||||
dayjs.extend(utc);
|
||||
|
||||
@ -521,165 +520,3 @@ export function common({ store, apolloClient }: ProcessContext) {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export function validation({ store, apolloClient }: ProcessContext) {
|
||||
const { $calculation } = store;
|
||||
|
||||
validateAgentRewardSumm(store, apolloClient, {
|
||||
rewardConditionField: 'selectIndAgentRewardCondition',
|
||||
rewardSummField: 'tbxIndAgentRewardSumm',
|
||||
});
|
||||
|
||||
validateAgentRewardSumm(store, apolloClient, {
|
||||
rewardConditionField: 'selectCalcDoubleAgentRewardCondition',
|
||||
rewardSummField: 'tbxCalcDoubleAgentRewardSumm',
|
||||
});
|
||||
|
||||
validateAgentRewardSumm(store, apolloClient, {
|
||||
rewardConditionField: 'selectCalcBrokerRewardCondition',
|
||||
rewardSummField: 'tbxCalcBrokerRewardSum',
|
||||
});
|
||||
|
||||
validateAgentRewardSumm(store, apolloClient, {
|
||||
rewardConditionField: 'selectFinDepartmentRewardCondtion',
|
||||
rewardSummField: 'tbxFinDepartmentRewardSumm',
|
||||
});
|
||||
|
||||
/**
|
||||
* Добавить валидацию на кнопку Рассчитать:
|
||||
* если tbxDealerRewardSumm > 0 и
|
||||
* если selectDealerPerson = selectDealerBroker и tbxDealerBrokerRewardSumm > 0, то ругаться на selectDealerPerson
|
||||
* если selectDealerPerson = selectIndAgent и tbxIndAgentRewardSumm > 0, то ругаться на selectDealerPerson
|
||||
* если selectDealerPerson = selectCalcDoubleAgent и tbxCalcDoubleAgentRewardSumm > 0, то ругаться на selectDealerPerson
|
||||
* если selectDealerPerson = selectCalcBroker tbxCalcBrokerRewardSum > 0, то ругаться на selectDealerPerson
|
||||
* если selectDealerPerson = selectFinDepartment и tbxFinDepartmentRewardSumm > 0, то ругаться на selectDealerPerson
|
||||
* 2.если tbxDealerBrokerRewardSumm > 0 и
|
||||
*
|
||||
* если selectDealerBroker = selectDealerPerson и tbxDealerRewardSumm > 0, то ругаться на selectDealerBroker
|
||||
* если selectDealerBroker = selectIndAgent и tbxIndAgentRewardSumm > 0, то ругаться на selectDealerBroker
|
||||
* если selectDealerBroker = selectCalcDoubleAgent и tbxCalcDoubleAgentRewardSumm > 0, то ругаться на selectDealerBroker
|
||||
* если selectDealerBroker = selectCalcBroker tbxCalcBrokerRewardSum > 0, то ругаться на selectDealerBroker
|
||||
* если selectDealerBroker = selectFinDepartment и tbxFinDepartmentRewardSumm > 0, то ругаться на selectDealerBroker
|
||||
* 3. если tbxIndAgentRewardSumm > 0 и
|
||||
*
|
||||
* если selectIndAgent = selectDealerPerson и tbxDealerRewardSumm > 0, то ругаться на selectIndAgent
|
||||
* если selectIndAgent = selectDealerBroker и tbxDealerBrokerRewardSumm > 0, то ругаться на selectIndAgent
|
||||
* если selectIndAgent = selectCalcDoubleAgent и tbxCalcDoubleAgentRewardSumm > 0, то ругаться на selectIndAgent
|
||||
* если selectIndAgent = selectCalcBroker tbxCalcBrokerRewardSum > 0, то ругаться на selectIndAgent
|
||||
* если selectIndAgent = selectFinDepartment и tbxFinDepartmentRewardSumm > 0, то ругаться на selectIndAgent
|
||||
* 4. если tbxCalcDoubleAgentRewardSumm > 0 и
|
||||
*
|
||||
* если selectCalcDoubleAgent = selectDealerPerson и tbxDealerRewardSumm > 0, то ругаться на selectCalcDoubleAgent
|
||||
* если selectCalcDoubleAgent = selectDealerBroker и tbxDealerBrokerRewardSumm > 0, то ругаться на selectCalcDoubleAgent
|
||||
* если selectCalcDoubleAgent = sselectIndAgent и tbxIndAgentRewardSumm > 0, то ругаться на selectCalcDoubleAgent
|
||||
* если selectCalcDoubleAgent = selectCalcBroker tbxCalcBrokerRewardSum > 0, то ругаться на selectCalcDoubleAgent
|
||||
* если selectCalcDoubleAgent = selectFinDepartment и tbxFinDepartmentRewardSumm > 0, то ругаться на selectCalcDoubleAgent
|
||||
* 5. если tbxCalcBrokerRewardSum > 0 и
|
||||
*
|
||||
* если selectCalcBroker = selectDealerPerson и tbxDealerRewardSumm > 0, то ругаться на selectCalcBroker
|
||||
* если selectCalcBroker = selectDealerBroker и tbxDealerBrokerRewardSumm > 0, то ругаться на selectCalcBroker
|
||||
* если selectCalcBroker = sselectIndAgent и tbxIndAgentRewardSumm > 0, то ругаться на selectCalcBroker
|
||||
* если selectCalcBroker = selectCalcDoubleAgent и tbxCalcDoubleAgentRewardSumm > 0, то ругаться на selectCalcBroker
|
||||
* если selectCalcBroker = selectFinDepartment и tbxFinDepartmentRewardSumm > 0, то ругаться на selectCalcBroker
|
||||
* 6. если tbxFinDepartmentRewardSumm > 0 и
|
||||
*
|
||||
* если selectFinDepartment = selectDealerPerson и tbxDealerRewardSumm > 0, то ругаться на selectFinDepartment
|
||||
* если selectFinDepartment = selectDealerBroker и tbxDealerBrokerRewardSumm > 0, то ругаться на selectFinDepartment
|
||||
* если selectFinDepartment = sselectIndAgent и tbxIndAgentRewardSumm > 0, то ругаться на selectFinDepartment
|
||||
* если selectFinDepartment = selectCalcDoubleAgent и tbxCalcDoubleAgentRewardSumm > 0, то ругаться на selectFinDepartment
|
||||
* если selectFinDepartment = selectCalcBroker tbxCalcBrokerRewardSum > 0, то ругаться на selectFinDepartment
|
||||
*/
|
||||
|
||||
// eslint-disable-next-line complexity, sonarjs/cognitive-complexity
|
||||
autorun(() => {
|
||||
const dealerRewardSumm = $calculation.element('tbxDealerRewardSumm').getValue();
|
||||
const dealerBrokerRewardSumm = $calculation.element('tbxDealerBrokerRewardSumm').getValue();
|
||||
const indAgentRewardSumm = $calculation.element('tbxIndAgentRewardSumm').getValue();
|
||||
const calcDoubleAgentRewardSumm = $calculation
|
||||
.element('tbxCalcDoubleAgentRewardSumm')
|
||||
.getValue();
|
||||
const calcBrokerRewardSum = $calculation.element('tbxCalcBrokerRewardSum').getValue();
|
||||
const finDepartmentRewardSumm = $calculation.element('tbxFinDepartmentRewardSumm').getValue();
|
||||
|
||||
const dealerPerson = $calculation.element('selectDealerPerson').getValue();
|
||||
const dealerBroker = $calculation.element('selectDealerBroker').getValue();
|
||||
const indAgent = $calculation.element('selectIndAgent').getValue();
|
||||
const calcDoubleAgent = $calculation.element('selectCalcDoubleAgent').getValue();
|
||||
const calcBroker = $calculation.element('selectCalcBroker').getValue();
|
||||
const calcFinDepartment = $calculation.element('selectCalcFinDepartment').getValue();
|
||||
|
||||
const message = 'Вы закладываете вознаграждение одному и тому же агенту дважды';
|
||||
|
||||
$calculation.element('selectDealerPerson').validate({
|
||||
invalid:
|
||||
dealerRewardSumm > 0 &&
|
||||
Boolean(dealerPerson) &&
|
||||
((dealerPerson === dealerBroker && dealerBrokerRewardSumm > 0) ||
|
||||
(dealerPerson === indAgent && indAgentRewardSumm > 0) ||
|
||||
(dealerPerson === calcDoubleAgent && calcDoubleAgentRewardSumm > 0) ||
|
||||
(dealerPerson === calcBroker && calcBrokerRewardSum > 0) ||
|
||||
(dealerPerson === calcFinDepartment && finDepartmentRewardSumm > 0)),
|
||||
message,
|
||||
});
|
||||
|
||||
$calculation.element('selectDealerBroker').validate({
|
||||
invalid:
|
||||
dealerBrokerRewardSumm > 0 &&
|
||||
Boolean(dealerBroker) &&
|
||||
((dealerBroker === dealerPerson && dealerRewardSumm > 0) ||
|
||||
(dealerBroker === indAgent && indAgentRewardSumm > 0) ||
|
||||
(dealerBroker === calcDoubleAgent && calcDoubleAgentRewardSumm > 0) ||
|
||||
(dealerBroker === calcBroker && calcBrokerRewardSum > 0) ||
|
||||
(dealerBroker === calcFinDepartment && finDepartmentRewardSumm > 0)),
|
||||
message,
|
||||
});
|
||||
|
||||
$calculation.element('selectIndAgent').validate({
|
||||
invalid:
|
||||
indAgentRewardSumm > 0 &&
|
||||
Boolean(indAgent) &&
|
||||
((indAgent === dealerPerson && dealerRewardSumm > 0) ||
|
||||
(indAgent === dealerBroker && dealerBrokerRewardSumm > 0) ||
|
||||
(indAgent === calcDoubleAgent && calcDoubleAgentRewardSumm > 0) ||
|
||||
(indAgent === calcBroker && calcBrokerRewardSum > 0) ||
|
||||
(indAgent === calcFinDepartment && finDepartmentRewardSumm > 0)),
|
||||
message,
|
||||
});
|
||||
|
||||
$calculation.element('selectCalcDoubleAgent').validate({
|
||||
invalid:
|
||||
calcDoubleAgentRewardSumm > 0 &&
|
||||
Boolean(calcDoubleAgent) &&
|
||||
((calcDoubleAgent === dealerPerson && dealerRewardSumm > 0) ||
|
||||
(calcDoubleAgent === dealerBroker && dealerBrokerRewardSumm > 0) ||
|
||||
(calcDoubleAgent === indAgent && indAgentRewardSumm > 0) ||
|
||||
(calcDoubleAgent === calcBroker && calcBrokerRewardSum > 0) ||
|
||||
(calcDoubleAgent === calcFinDepartment && finDepartmentRewardSumm > 0)),
|
||||
message,
|
||||
});
|
||||
|
||||
$calculation.element('selectCalcBroker').validate({
|
||||
invalid:
|
||||
calcBrokerRewardSum > 0 &&
|
||||
Boolean(calcBroker) &&
|
||||
((calcBroker === dealerPerson && dealerRewardSumm > 0) ||
|
||||
(calcBroker === dealerBroker && dealerBrokerRewardSumm > 0) ||
|
||||
(calcBroker === indAgent && indAgentRewardSumm > 0) ||
|
||||
(calcBroker === calcDoubleAgent && calcDoubleAgentRewardSumm > 0) ||
|
||||
(calcBroker === calcFinDepartment && finDepartmentRewardSumm > 0)),
|
||||
message,
|
||||
});
|
||||
|
||||
$calculation.element('selectCalcFinDepartment').validate({
|
||||
invalid:
|
||||
finDepartmentRewardSumm > 0 &&
|
||||
Boolean(calcFinDepartment) &&
|
||||
((calcFinDepartment === dealerPerson && dealerRewardSumm > 0) ||
|
||||
(calcFinDepartment === dealerBroker && dealerBrokerRewardSumm > 0) ||
|
||||
(calcFinDepartment === indAgent && indAgentRewardSumm > 0) ||
|
||||
(calcFinDepartment === calcDoubleAgent && calcDoubleAgentRewardSumm > 0) ||
|
||||
(calcFinDepartment === calcBroker && calcBrokerRewardSum > 0)),
|
||||
message,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@ -9,9 +9,6 @@ function common(context: ProcessContext) {
|
||||
supplier.common(context);
|
||||
}
|
||||
|
||||
function validation(context: ProcessContext) {
|
||||
agents.validation(context);
|
||||
supplier.validation(context);
|
||||
}
|
||||
export { common };
|
||||
|
||||
export { common, validation };
|
||||
export { default as validation } from './validation';
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import * as CRMTypes from '@/graphql/crm.types';
|
||||
import type { ProcessContext } from '@/process/types';
|
||||
import { autorun, reaction } from 'mobx';
|
||||
import { reaction } from 'mobx';
|
||||
|
||||
export function common({ store, apolloClient }: ProcessContext) {
|
||||
const { $calculation, $tables } = store;
|
||||
@ -73,37 +73,4 @@ export function common({ store, apolloClient }: ProcessContext) {
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* В валидацию на кнопку Рассчитать внести изменение:
|
||||
* 1) поле selectDealerPerson убрать из списка обязательных для расчета полей
|
||||
* 2) добавить валидацию на поле selectDealerPerson :
|
||||
* Если в поле selectDealer указан account, у которого evo_return_leasing_dealer = False (или null)
|
||||
* и поле selectDealerPerson = null, то выводить ошибку и поле selectDealerPerson обводить красной рамкой,
|
||||
* иначе все ок
|
||||
*/
|
||||
|
||||
autorun(async () => {
|
||||
const dealerId = $calculation.element('selectDealer').getValue();
|
||||
const dealerPersonId = $calculation.element('selectDealerPerson').getValue();
|
||||
|
||||
let returnLeasing: boolean | null | undefined;
|
||||
|
||||
if (dealerId) {
|
||||
const {
|
||||
data: { dealer },
|
||||
} = await apolloClient.query({
|
||||
query: CRMTypes.GetDealerDocument,
|
||||
variables: {
|
||||
dealerId,
|
||||
},
|
||||
});
|
||||
returnLeasing = dealer?.evo_return_leasing_dealer;
|
||||
}
|
||||
|
||||
$calculation.element('selectDealerPerson').validate({
|
||||
invalid: Boolean(dealerId) && !dealerPersonId && !returnLeasing,
|
||||
message: 'Не заполнено поле',
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@ -157,15 +157,3 @@ export function common({ store, apolloClient }: ProcessContext) {
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export function validation({ store, apolloClient }: ProcessContext) {
|
||||
createReactions.validateAgentRewardSumm(store, apolloClient, {
|
||||
rewardConditionField: 'selectDealerRewardCondition',
|
||||
rewardSummField: 'tbxDealerRewardSumm',
|
||||
});
|
||||
|
||||
createReactions.validateAgentRewardSumm(store, apolloClient, {
|
||||
rewardConditionField: 'selectDealerBrokerRewardCondition',
|
||||
rewardSummField: 'tbxDealerBrokerRewardSumm',
|
||||
});
|
||||
}
|
||||
|
||||
70
apps/web/process/supplier-agent/reactions/validation.ts
Normal file
70
apps/web/process/supplier-agent/reactions/validation.ts
Normal file
@ -0,0 +1,70 @@
|
||||
import { createValidationSchema } from '../validation';
|
||||
import type { Elements } from '@/Components/Calculation/config/map/values';
|
||||
import type { ProcessContext } from '@/process/types';
|
||||
import ValidationHelper from '@/stores/validation/helper';
|
||||
import { reaction } from 'mobx';
|
||||
import { uid } from 'radash';
|
||||
|
||||
const key = uid(7);
|
||||
|
||||
export default function reactions(context: ProcessContext) {
|
||||
const { store } = context;
|
||||
const { $calculation } = store;
|
||||
const validationSchema = createValidationSchema(context);
|
||||
|
||||
const helper = new ValidationHelper();
|
||||
reaction(
|
||||
() => {
|
||||
const values = $calculation.$values.getValues([
|
||||
'calcBrokerRewardCondition',
|
||||
'calcBrokerRewardSum',
|
||||
'calcDoubleAgentRewardCondition',
|
||||
'calcDoubleAgentRewardSumm',
|
||||
'dealer',
|
||||
'dealerBroker',
|
||||
'calcFinDepartment',
|
||||
'dealerBrokerRewardCondition',
|
||||
'dealerBrokerRewardSumm',
|
||||
'dealerPerson',
|
||||
'dealerRewardCondition',
|
||||
'dealerRewardSumm',
|
||||
'finDepartmentRewardCondtion',
|
||||
'finDepartmentRewardSumm',
|
||||
'indAgent',
|
||||
'indAgentRewardCondition',
|
||||
'indAgentRewardSumm',
|
||||
'calcDoubleAgent',
|
||||
'calcBroker',
|
||||
]);
|
||||
|
||||
const options = (
|
||||
[
|
||||
'selectCalcBrokerRewardCondition',
|
||||
'selectCalcDoubleAgentRewardCondition',
|
||||
'selectDealerBrokerRewardCondition',
|
||||
'selectDealerRewardCondition',
|
||||
'selectFinDepartmentRewardCondtion',
|
||||
'selectIndAgentRewardCondition',
|
||||
] as Elements[]
|
||||
).map((elementName) => $calculation.element(elementName).getOptions());
|
||||
|
||||
return { options, values };
|
||||
},
|
||||
async ({ values }) => {
|
||||
helper.removeErrors();
|
||||
const validationResult = await validationSchema.safeParseAsync(values);
|
||||
|
||||
if (!validationResult.success) {
|
||||
validationResult.error.errors.forEach(({ path, message }) => {
|
||||
(path as Elements[]).forEach((elementName) => {
|
||||
const removeError = $calculation.element(elementName).setError({ key, message });
|
||||
if (removeError) helper.add(removeError);
|
||||
});
|
||||
});
|
||||
}
|
||||
},
|
||||
{
|
||||
delay: 100,
|
||||
}
|
||||
);
|
||||
}
|
||||
388
apps/web/process/supplier-agent/validation.ts
Normal file
388
apps/web/process/supplier-agent/validation.ts
Normal file
@ -0,0 +1,388 @@
|
||||
/* eslint-disable zod/require-strict */
|
||||
/* eslint-disable @typescript-eslint/naming-convention */
|
||||
/* eslint-disable complexity */
|
||||
import type { ValidationContext } from '../types';
|
||||
import type { Elements } from '@/Components/Calculation/config/map/values';
|
||||
import ValuesSchema from '@/config/schema/values';
|
||||
import * as CRMTypes from '@/graphql/crm.types';
|
||||
import dayjs from 'dayjs';
|
||||
import utc from 'dayjs/plugin/utc';
|
||||
import { normalizeOptions } from 'tools';
|
||||
import type { RefinementCtx } from 'zod';
|
||||
import { z } from 'zod';
|
||||
|
||||
dayjs.extend(utc);
|
||||
|
||||
function helper({ apolloClient, ctx }: ValidationContext & { ctx: RefinementCtx }) {
|
||||
return {
|
||||
async validateRewardSum({
|
||||
agentid,
|
||||
conditionId,
|
||||
sumFieldName,
|
||||
sum,
|
||||
}: {
|
||||
agentid: string | null;
|
||||
conditionId: string | null;
|
||||
sum: number;
|
||||
sumFieldName: Elements;
|
||||
}) {
|
||||
if (agentid) {
|
||||
const {
|
||||
data: { evo_reward_conditions },
|
||||
} = await apolloClient.query({
|
||||
query: CRMTypes.GetRewardConditionsDocument,
|
||||
variables: {
|
||||
agentid,
|
||||
currentDate: dayjs().utc(false).format('YYYY-MM-DD'),
|
||||
},
|
||||
});
|
||||
|
||||
const requests = normalizeOptions(evo_reward_conditions)?.map(async ({ value }) => {
|
||||
const {
|
||||
data: { evo_reward_condition },
|
||||
} = await apolloClient.query<
|
||||
CRMTypes.GetRewardConditionQuery,
|
||||
CRMTypes.GetRewardConditionQueryVariables
|
||||
>({
|
||||
query: CRMTypes.GetRewardConditionDocument,
|
||||
variables: {
|
||||
conditionId: value,
|
||||
},
|
||||
});
|
||||
|
||||
return evo_reward_condition;
|
||||
});
|
||||
|
||||
const requiredReward = (await Promise.all(requests)).some(
|
||||
(x) => x?.evo_agency_agreementidData?.evo_required_reward === true
|
||||
);
|
||||
|
||||
if (sum === 0 && requiredReward) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: 'Согласно агентскому договору обязательна выплата АВ. Заложите АВ в расчет',
|
||||
path: [sumFieldName],
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (conditionId) {
|
||||
const {
|
||||
data: { evo_reward_condition },
|
||||
} = await apolloClient.query<
|
||||
CRMTypes.GetRewardConditionQuery,
|
||||
CRMTypes.GetRewardConditionQueryVariables
|
||||
>({
|
||||
query: CRMTypes.GetRewardConditionDocument,
|
||||
variables: {
|
||||
conditionId,
|
||||
},
|
||||
});
|
||||
|
||||
if (evo_reward_condition?.evo_reward_summ && sum > evo_reward_condition?.evo_reward_summ) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: 'Вознаграждение указано больше условия по агентскому договору!',
|
||||
path: [sumFieldName],
|
||||
});
|
||||
}
|
||||
|
||||
if (
|
||||
evo_reward_condition?.evo_reduce_reward === false &&
|
||||
evo_reward_condition?.evo_reward_summ &&
|
||||
sum < evo_reward_condition?.evo_reward_summ
|
||||
) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: 'Вознаграждение указано меньше условия по агентскому договору!',
|
||||
path: [sumFieldName],
|
||||
});
|
||||
}
|
||||
|
||||
if (
|
||||
evo_reward_condition?.evo_min_reward_summ &&
|
||||
sum < evo_reward_condition?.evo_min_reward_summ
|
||||
) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: 'Вознаграждение указано меньше условия по агентскому договору!',
|
||||
path: [sumFieldName],
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
const ERR_DOUBLE_REWARD = 'Вы закладываете вознаграждение одному и тому же агенту дважды';
|
||||
|
||||
const AgentsSchema = ValuesSchema.pick({
|
||||
calcBroker: true,
|
||||
calcDoubleAgent: true,
|
||||
calcFinDepartment: true,
|
||||
dealer: true,
|
||||
dealerBroker: true,
|
||||
dealerPerson: true,
|
||||
indAgent: true,
|
||||
});
|
||||
|
||||
const ConditionsSchema = ValuesSchema.pick({
|
||||
calcBrokerRewardCondition: true,
|
||||
calcDoubleAgentRewardCondition: true,
|
||||
dealerBrokerRewardCondition: true,
|
||||
dealerRewardCondition: true,
|
||||
finDepartmentRewardCondtion: true,
|
||||
indAgentRewardCondition: true,
|
||||
});
|
||||
|
||||
const SumsSchema = ValuesSchema.pick({
|
||||
calcBrokerRewardSum: true,
|
||||
calcDoubleAgentRewardSumm: true,
|
||||
dealerBrokerRewardSumm: true,
|
||||
dealerRewardSumm: true,
|
||||
finDepartmentRewardSumm: true,
|
||||
indAgentRewardSumm: true,
|
||||
});
|
||||
|
||||
export function createValidationSchema(context: ValidationContext) {
|
||||
const { apolloClient } = context;
|
||||
|
||||
return z
|
||||
.object({})
|
||||
.merge(AgentsSchema)
|
||||
.merge(ConditionsSchema)
|
||||
.merge(SumsSchema)
|
||||
.superRefine(async (values, ctx) => {
|
||||
const {
|
||||
calcBrokerRewardCondition,
|
||||
calcBrokerRewardSum,
|
||||
calcDoubleAgentRewardCondition,
|
||||
calcDoubleAgentRewardSumm,
|
||||
dealer: dealerId,
|
||||
dealerBroker,
|
||||
calcFinDepartment,
|
||||
dealerBrokerRewardCondition,
|
||||
dealerBrokerRewardSumm,
|
||||
dealerPerson,
|
||||
dealerRewardCondition,
|
||||
dealerRewardSumm,
|
||||
finDepartmentRewardCondtion,
|
||||
finDepartmentRewardSumm,
|
||||
indAgent,
|
||||
indAgentRewardCondition,
|
||||
indAgentRewardSumm,
|
||||
calcDoubleAgent,
|
||||
calcBroker,
|
||||
} = values;
|
||||
/**
|
||||
* Добавить валидацию на кнопку Рассчитать:
|
||||
* если tbxDealerRewardSumm > 0 и
|
||||
* если selectDealerPerson = selectDealerBroker и tbxDealerBrokerRewardSumm > 0, то ругаться на selectDealerPerson
|
||||
* если selectDealerPerson = selectIndAgent и tbxIndAgentRewardSumm > 0, то ругаться на selectDealerPerson
|
||||
* если selectDealerPerson = selectCalcDoubleAgent и tbxCalcDoubleAgentRewardSumm > 0, то ругаться на selectDealerPerson
|
||||
* если selectDealerPerson = selectCalcBroker tbxCalcBrokerRewardSum > 0, то ругаться на selectDealerPerson
|
||||
* если selectDealerPerson = selectFinDepartment и tbxFinDepartmentRewardSumm > 0, то ругаться на selectDealerPerson
|
||||
* 2.если tbxDealerBrokerRewardSumm > 0 и
|
||||
*
|
||||
* если selectDealerBroker = selectDealerPerson и tbxDealerRewardSumm > 0, то ругаться на selectDealerBroker
|
||||
* если selectDealerBroker = selectIndAgent и tbxIndAgentRewardSumm > 0, то ругаться на selectDealerBroker
|
||||
* если selectDealerBroker = selectCalcDoubleAgent и tbxCalcDoubleAgentRewardSumm > 0, то ругаться на selectDealerBroker
|
||||
* если selectDealerBroker = selectCalcBroker tbxCalcBrokerRewardSum > 0, то ругаться на selectDealerBroker
|
||||
* если selectDealerBroker = selectFinDepartment и tbxFinDepartmentRewardSumm > 0, то ругаться на selectDealerBroker
|
||||
* 3. если tbxIndAgentRewardSumm > 0 и
|
||||
*
|
||||
* если selectIndAgent = selectDealerPerson и tbxDealerRewardSumm > 0, то ругаться на selectIndAgent
|
||||
* если selectIndAgent = selectDealerBroker и tbxDealerBrokerRewardSumm > 0, то ругаться на selectIndAgent
|
||||
* если selectIndAgent = selectCalcDoubleAgent и tbxCalcDoubleAgentRewardSumm > 0, то ругаться на selectIndAgent
|
||||
* если selectIndAgent = selectCalcBroker tbxCalcBrokerRewardSum > 0, то ругаться на selectIndAgent
|
||||
* если selectIndAgent = selectFinDepartment и tbxFinDepartmentRewardSumm > 0, то ругаться на selectIndAgent
|
||||
* 4. если tbxCalcDoubleAgentRewardSumm > 0 и
|
||||
*
|
||||
* если selectCalcDoubleAgent = selectDealerPerson и tbxDealerRewardSumm > 0, то ругаться на selectCalcDoubleAgent
|
||||
* если selectCalcDoubleAgent = selectDealerBroker и tbxDealerBrokerRewardSumm > 0, то ругаться на selectCalcDoubleAgent
|
||||
* если selectCalcDoubleAgent = sselectIndAgent и tbxIndAgentRewardSumm > 0, то ругаться на selectCalcDoubleAgent
|
||||
* если selectCalcDoubleAgent = selectCalcBroker tbxCalcBrokerRewardSum > 0, то ругаться на selectCalcDoubleAgent
|
||||
* если selectCalcDoubleAgent = selectFinDepartment и tbxFinDepartmentRewardSumm > 0, то ругаться на selectCalcDoubleAgent
|
||||
* 5. если tbxCalcBrokerRewardSum > 0 и
|
||||
*
|
||||
* если selectCalcBroker = selectDealerPerson и tbxDealerRewardSumm > 0, то ругаться на selectCalcBroker
|
||||
* если selectCalcBroker = selectDealerBroker и tbxDealerBrokerRewardSumm > 0, то ругаться на selectCalcBroker
|
||||
* если selectCalcBroker = sselectIndAgent и tbxIndAgentRewardSumm > 0, то ругаться на selectCalcBroker
|
||||
* если selectCalcBroker = selectCalcDoubleAgent и tbxCalcDoubleAgentRewardSumm > 0, то ругаться на selectCalcBroker
|
||||
* если selectCalcBroker = selectFinDepartment и tbxFinDepartmentRewardSumm > 0, то ругаться на selectCalcBroker
|
||||
* 6. если tbxFinDepartmentRewardSumm > 0 и
|
||||
*
|
||||
* если selectFinDepartment = selectDealerPerson и tbxDealerRewardSumm > 0, то ругаться на selectFinDepartment
|
||||
* если selectFinDepartment = selectDealerBroker и tbxDealerBrokerRewardSumm > 0, то ругаться на selectFinDepartment
|
||||
* если selectFinDepartment = sselectIndAgent и tbxIndAgentRewardSumm > 0, то ругаться на selectFinDepartment
|
||||
* если selectFinDepartment = selectCalcDoubleAgent и tbxCalcDoubleAgentRewardSumm > 0, то ругаться на selectFinDepartment
|
||||
* если selectFinDepartment = selectCalcBroker tbxCalcBrokerRewardSum > 0, то ругаться на selectFinDepartment
|
||||
*/
|
||||
|
||||
if (
|
||||
dealerRewardSumm > 0 &&
|
||||
Boolean(dealerPerson) &&
|
||||
((dealerPerson === dealerBroker && dealerBrokerRewardSumm > 0) ||
|
||||
(dealerPerson === indAgent && indAgentRewardSumm > 0) ||
|
||||
(dealerPerson === calcDoubleAgent && calcDoubleAgentRewardSumm > 0) ||
|
||||
(dealerPerson === calcBroker && calcBrokerRewardSum > 0) ||
|
||||
(dealerPerson === calcFinDepartment && finDepartmentRewardSumm > 0))
|
||||
) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: ERR_DOUBLE_REWARD,
|
||||
path: ['selectDealerPerson'],
|
||||
});
|
||||
}
|
||||
|
||||
if (
|
||||
dealerBrokerRewardSumm > 0 &&
|
||||
Boolean(dealerBroker) &&
|
||||
((dealerBroker === dealerPerson && dealerRewardSumm > 0) ||
|
||||
(dealerBroker === indAgent && indAgentRewardSumm > 0) ||
|
||||
(dealerBroker === calcDoubleAgent && calcDoubleAgentRewardSumm > 0) ||
|
||||
(dealerBroker === calcBroker && calcBrokerRewardSum > 0) ||
|
||||
(dealerBroker === calcFinDepartment && finDepartmentRewardSumm > 0))
|
||||
) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: ERR_DOUBLE_REWARD,
|
||||
path: ['selectDealerBroker'],
|
||||
});
|
||||
}
|
||||
|
||||
if (
|
||||
indAgentRewardSumm > 0 &&
|
||||
Boolean(indAgent) &&
|
||||
((indAgent === dealerPerson && dealerRewardSumm > 0) ||
|
||||
(indAgent === dealerBroker && dealerBrokerRewardSumm > 0) ||
|
||||
(indAgent === calcDoubleAgent && calcDoubleAgentRewardSumm > 0) ||
|
||||
(indAgent === calcBroker && calcBrokerRewardSum > 0) ||
|
||||
(indAgent === calcFinDepartment && finDepartmentRewardSumm > 0))
|
||||
) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: ERR_DOUBLE_REWARD,
|
||||
path: ['selectIndAgent'],
|
||||
});
|
||||
}
|
||||
|
||||
if (
|
||||
calcDoubleAgentRewardSumm > 0 &&
|
||||
Boolean(calcDoubleAgent) &&
|
||||
((calcDoubleAgent === dealerPerson && dealerRewardSumm > 0) ||
|
||||
(calcDoubleAgent === dealerBroker && dealerBrokerRewardSumm > 0) ||
|
||||
(calcDoubleAgent === indAgent && indAgentRewardSumm > 0) ||
|
||||
(calcDoubleAgent === calcBroker && calcBrokerRewardSum > 0) ||
|
||||
(calcDoubleAgent === calcFinDepartment && finDepartmentRewardSumm > 0))
|
||||
) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: ERR_DOUBLE_REWARD,
|
||||
path: ['selectCalcDoubleAgent'],
|
||||
});
|
||||
}
|
||||
|
||||
if (
|
||||
calcBrokerRewardSum > 0 &&
|
||||
Boolean(calcBroker) &&
|
||||
((calcBroker === dealerPerson && dealerRewardSumm > 0) ||
|
||||
(calcBroker === dealerBroker && dealerBrokerRewardSumm > 0) ||
|
||||
(calcBroker === indAgent && indAgentRewardSumm > 0) ||
|
||||
(calcBroker === calcDoubleAgent && calcDoubleAgentRewardSumm > 0) ||
|
||||
(calcBroker === calcFinDepartment && finDepartmentRewardSumm > 0))
|
||||
) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: ERR_DOUBLE_REWARD,
|
||||
path: ['selectCalcBroker'],
|
||||
});
|
||||
}
|
||||
|
||||
if (
|
||||
finDepartmentRewardSumm > 0 &&
|
||||
Boolean(calcFinDepartment) &&
|
||||
((calcFinDepartment === dealerPerson && dealerRewardSumm > 0) ||
|
||||
(calcFinDepartment === dealerBroker && dealerBrokerRewardSumm > 0) ||
|
||||
(calcFinDepartment === indAgent && indAgentRewardSumm > 0) ||
|
||||
(calcFinDepartment === calcDoubleAgent && calcDoubleAgentRewardSumm > 0) ||
|
||||
(calcFinDepartment === calcBroker && calcBrokerRewardSum > 0))
|
||||
) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: ERR_DOUBLE_REWARD,
|
||||
path: ['selectCalcFinDepartment'],
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* В валидацию на кнопку Рассчитать внести изменение:
|
||||
* 1) поле selectDealerPerson убрать из списка обязательных для расчета полей
|
||||
* 2) добавить валидацию на поле selectDealerPerson :
|
||||
* Если в поле selectDealer указан account, у которого evo_return_leasing_dealer = False (или null)
|
||||
* и поле selectDealerPerson = null, то выводить ошибку и поле selectDealerPerson обводить красной рамкой,
|
||||
* иначе все ок
|
||||
*/
|
||||
if (dealerId) {
|
||||
const {
|
||||
data: { dealer },
|
||||
} = await apolloClient.query({
|
||||
query: CRMTypes.GetDealerDocument,
|
||||
variables: {
|
||||
dealerId,
|
||||
},
|
||||
});
|
||||
|
||||
if (!dealerPerson && !dealer?.evo_return_leasing_dealer)
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: 'Не заполнено поле',
|
||||
path: ['selectDealerPerson'],
|
||||
});
|
||||
}
|
||||
|
||||
const { validateRewardSum } = helper({ ...context, ctx });
|
||||
|
||||
await validateRewardSum({
|
||||
agentid: dealerPerson,
|
||||
conditionId: dealerRewardCondition,
|
||||
sum: dealerRewardSumm,
|
||||
sumFieldName: 'tbxDealerRewardSumm',
|
||||
});
|
||||
|
||||
await validateRewardSum({
|
||||
agentid: dealerBroker,
|
||||
conditionId: dealerBrokerRewardCondition,
|
||||
sum: dealerBrokerRewardSumm,
|
||||
sumFieldName: 'tbxDealerBrokerRewardSumm',
|
||||
});
|
||||
|
||||
await validateRewardSum({
|
||||
agentid: indAgent,
|
||||
conditionId: indAgentRewardCondition,
|
||||
sum: indAgentRewardSumm,
|
||||
sumFieldName: 'tbxIndAgentRewardSumm',
|
||||
});
|
||||
|
||||
await validateRewardSum({
|
||||
agentid: calcDoubleAgent,
|
||||
conditionId: calcDoubleAgentRewardCondition,
|
||||
sum: calcDoubleAgentRewardSumm,
|
||||
sumFieldName: 'tbxCalcDoubleAgentRewardSumm',
|
||||
});
|
||||
|
||||
await validateRewardSum({
|
||||
agentid: calcBroker,
|
||||
conditionId: calcBrokerRewardCondition,
|
||||
sum: calcBrokerRewardSum,
|
||||
sumFieldName: 'tbxCalcBrokerRewardSum',
|
||||
});
|
||||
|
||||
await validateRewardSum({
|
||||
agentid: calcFinDepartment,
|
||||
conditionId: finDepartmentRewardCondtion,
|
||||
sum: finDepartmentRewardSumm,
|
||||
sumFieldName: 'tbxFinDepartmentRewardSumm',
|
||||
});
|
||||
});
|
||||
}
|
||||
@ -13,3 +13,5 @@ export type ProcessContext = {
|
||||
export type Process = {
|
||||
reactions: Record<string, (context: ProcessContext) => void>;
|
||||
};
|
||||
|
||||
export type ValidationContext = Omit<ProcessContext, 'store'>;
|
||||
|
||||
@ -1,8 +1,11 @@
|
||||
/* eslint-disable @typescript-eslint/naming-convention */
|
||||
import type { ProcessContext } from '../types';
|
||||
import { createValidationSchema } from './validation';
|
||||
import type { Elements } from '@/Components/Calculation/config/map/values';
|
||||
import * as CRMTypes from '@/graphql/crm.types';
|
||||
import ValidationHelper from '@/stores/validation/helper';
|
||||
import { reaction } from 'mobx';
|
||||
import { uid } from 'radash';
|
||||
|
||||
export function common({ store, apolloClient }: ProcessContext) {
|
||||
const { $calculation } = store;
|
||||
@ -150,41 +153,6 @@ export function common({ store, apolloClient }: ProcessContext) {
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* Добавить реакцию на изменение Типа ПЛ selectLeaseObjectType , ПЛ БУ cbxLeaseObjectUsed и Моточасы tbxEngineHours:
|
||||
* Если ПЛ БУ cbxLeaseObjectUsed = True и Тип ПЛ selectLeaseObjectType = Спецтехника (id=9) и Моточасы = 0,
|
||||
* то поле Моточасы tbxEngineHours должно обводиться красной рамкой и выводиться сообщение
|
||||
* "Укажите Моточасы, иначе красная рамка снимается.
|
||||
* При красной рамке в данном поле нельзя осуществить расчет графика.
|
||||
*/
|
||||
{
|
||||
const validationHelper = new ValidationHelper();
|
||||
|
||||
reaction(
|
||||
() => $calculation.$values.getValues(['leaseObjectUsed', 'engineHours', 'leaseObjectType']),
|
||||
async ({ leaseObjectType: leaseObjectTypeId, leaseObjectUsed, engineHours }) => {
|
||||
if (!leaseObjectTypeId) {
|
||||
validationHelper.removeErrors();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const {
|
||||
data: { evo_leasingobject_type },
|
||||
} = await apolloClient.query({
|
||||
query: CRMTypes.GetLeaseObjectTypeDocument,
|
||||
variables: { leaseObjectTypeId },
|
||||
});
|
||||
|
||||
$calculation.element('tbxEngineHours').validate({
|
||||
helper: validationHelper,
|
||||
invalid: leaseObjectUsed && evo_leasingobject_type?.evo_id === '9' && !engineHours,
|
||||
message: 'Не заполнено поле',
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
reaction(
|
||||
() => $calculation.element('cbxLeaseObjectUsed').getValue(),
|
||||
(leaseObjectUsed) => {
|
||||
@ -196,16 +164,6 @@ export function common({ store, apolloClient }: ProcessContext) {
|
||||
}
|
||||
);
|
||||
|
||||
reaction(
|
||||
() => $calculation.$values.getValues(['mileage', 'leaseObjectUsed']),
|
||||
({ mileage, leaseObjectUsed }) => {
|
||||
$calculation.element('tbxMileage').validate({
|
||||
invalid: leaseObjectUsed && !mileage,
|
||||
message: 'Не заполнено поле',
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
reaction(
|
||||
() => $calculation.element('cbxLeaseObjectUsed').getValue(),
|
||||
(leaseObjectUsed) => {
|
||||
@ -273,38 +231,6 @@ export function common({ store, apolloClient }: ProcessContext) {
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* Если "Категория" содержит данные, то должны быть доступными для набора только арабские цифры и буквы латинского алфавита за исключением I, O, Q, так как они сходны по начертанию с цифрами 1, 0, 9. Можно использовать регулярное выражение: "^[A-HJ-NPR-Za-hj-npr-z0-9]{17}$".
|
||||
* Иначе (если Категория = пусто) то требуется аналогичная первому условию маска, но без проверки 17ти символов (допускать и больше и меньше символов, мб так: "^[A-HJ-NPR-Za-hj-npr-z0-9]{99}$".
|
||||
* Вот так: /^[A-HJ-NPR-Za-hj-npr-z0-9]+$/
|
||||
*/
|
||||
{
|
||||
const vinRegex = /^[\dA-HJ-NPR-Za-hj-npr-z]+$/u;
|
||||
const validationHelper = new ValidationHelper();
|
||||
|
||||
reaction(
|
||||
() => $calculation.$values.getValues(['vin', 'leaseObjectCategory', 'leaseObjectUsed']),
|
||||
({ vin, leaseObjectCategory }) => {
|
||||
if (!vin) {
|
||||
validationHelper.removeErrors();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
let invalid = vinRegex.test(vin) === false;
|
||||
if (leaseObjectCategory && vin?.length !== 17) {
|
||||
invalid = true;
|
||||
}
|
||||
|
||||
$calculation.element('tbxVIN').validate({
|
||||
helper: validationHelper,
|
||||
invalid,
|
||||
message: 'Неверно заполнено поле',
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
reaction(
|
||||
() => $calculation.element('cbxLeaseObjectUsed').getValue(),
|
||||
(leaseObjectUsed) => {
|
||||
@ -316,3 +242,40 @@ export function common({ store, apolloClient }: ProcessContext) {
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
const key = uid(7);
|
||||
|
||||
export function validation(context: ProcessContext) {
|
||||
const { store } = context;
|
||||
const { $calculation } = store;
|
||||
const validationSchema = createValidationSchema(context);
|
||||
|
||||
const helper = new ValidationHelper();
|
||||
reaction(
|
||||
() =>
|
||||
$calculation.$values.getValues([
|
||||
'engineHours',
|
||||
'leaseObjectCategory',
|
||||
'leaseObjectType',
|
||||
'leaseObjectUsed',
|
||||
'mileage',
|
||||
'vin',
|
||||
]),
|
||||
async (values) => {
|
||||
helper.removeErrors();
|
||||
const validationResult = await validationSchema.safeParseAsync(values);
|
||||
|
||||
if (!validationResult.success) {
|
||||
validationResult.error.errors.forEach(({ path, message }) => {
|
||||
(path as Elements[]).forEach((elementName) => {
|
||||
const removeError = $calculation.element(elementName).setError({ key, message });
|
||||
if (removeError) helper.add(removeError);
|
||||
});
|
||||
});
|
||||
}
|
||||
},
|
||||
{
|
||||
delay: 100,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
81
apps/web/process/used-pl/validation.ts
Normal file
81
apps/web/process/used-pl/validation.ts
Normal file
@ -0,0 +1,81 @@
|
||||
/* eslint-disable @typescript-eslint/naming-convention */
|
||||
import type { ValidationContext } from '../types';
|
||||
import ValuesSchema from '@/config/schema/values';
|
||||
import * as CRMTypes from '@/graphql/crm.types';
|
||||
import { z } from 'zod';
|
||||
|
||||
const vinRegex = /^[\dA-HJ-NPR-Za-hj-npr-z]+$/u;
|
||||
|
||||
export function createValidationSchema({ apolloClient }: ValidationContext) {
|
||||
return ValuesSchema.pick({
|
||||
engineHours: true,
|
||||
leaseObjectCategory: true,
|
||||
leaseObjectType: true,
|
||||
leaseObjectUsed: true,
|
||||
mileage: true,
|
||||
vin: true,
|
||||
}).superRefine(
|
||||
async (
|
||||
{
|
||||
engineHours,
|
||||
leaseObjectCategory,
|
||||
leaseObjectType: leaseObjectTypeId,
|
||||
leaseObjectUsed,
|
||||
mileage,
|
||||
vin,
|
||||
},
|
||||
ctx
|
||||
) => {
|
||||
/**
|
||||
* Добавить реакцию на изменение Типа ПЛ selectLeaseObjectType , ПЛ БУ cbxLeaseObjectUsed и Моточасы tbxEngineHours:
|
||||
* Если ПЛ БУ cbxLeaseObjectUsed = True и Тип ПЛ selectLeaseObjectType = Спецтехника (id=9) и Моточасы = 0,
|
||||
* то поле Моточасы tbxEngineHours должно обводиться красной рамкой и выводиться сообщение
|
||||
* "Укажите Моточасы, иначе красная рамка снимается.
|
||||
* При красной рамке в данном поле нельзя осуществить расчет графика.
|
||||
*/
|
||||
if (leaseObjectTypeId) {
|
||||
const {
|
||||
data: { evo_leasingobject_type },
|
||||
} = await apolloClient.query({
|
||||
query: CRMTypes.GetLeaseObjectTypeDocument,
|
||||
variables: { leaseObjectTypeId },
|
||||
});
|
||||
if (leaseObjectUsed && evo_leasingobject_type?.evo_id === '9' && !engineHours) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: 'Не заполнено поле',
|
||||
path: ['tbxEngineHours'],
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (leaseObjectUsed && !mileage) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: 'Не заполнено поле',
|
||||
path: ['tbxMileage'],
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Если "Категория" содержит данные, то должны быть доступными для набора только арабские цифры и буквы латинского алфавита за исключением I, O, Q, так как они сходны по начертанию с цифрами 1, 0, 9. Можно использовать регулярное выражение: "^[A-HJ-NPR-Za-hj-npr-z0-9]{17}$".
|
||||
* Иначе (если Категория = пусто) то требуется аналогичная первому условию маска, но без проверки 17ти символов (допускать и больше и меньше символов, мб так: "^[A-HJ-NPR-Za-hj-npr-z0-9]{99}$".
|
||||
* Вот так: /^[A-HJ-NPR-Za-hj-npr-z0-9]+$/
|
||||
*/
|
||||
if (vin) {
|
||||
let invalid = vinRegex.test(vin) === false;
|
||||
if (leaseObjectCategory && vin?.length !== 17) {
|
||||
invalid = true;
|
||||
}
|
||||
|
||||
if (invalid) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: 'Неверно заполнено поле',
|
||||
path: ['tbxVIN'],
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
import type { RemoveError, ValidationParams } from '../validation/types';
|
||||
import type { ValidationParams } from '../validation/types';
|
||||
import OptionsStore from './options';
|
||||
import StatusStore from './statuses';
|
||||
import ValuesStore from './values';
|
||||
@ -56,6 +56,10 @@ export default class CalculationStore {
|
||||
return this.$values.getValue(valueName) as Values.ElementsTypes[E];
|
||||
},
|
||||
|
||||
removeError: (params: Pick<ValidationParams, 'key'>) => {
|
||||
this.$validation[elementName]?.removeError(params);
|
||||
},
|
||||
|
||||
reset: () => {
|
||||
const valueName = getValueName(elementName);
|
||||
this.$values.resetValue(valueName);
|
||||
@ -80,6 +84,12 @@ export default class CalculationStore {
|
||||
return this.element(elementName);
|
||||
},
|
||||
|
||||
setError: (params: ValidationParams) => {
|
||||
if (!this.$validation[elementName]) this.createElementValidation(elementName);
|
||||
|
||||
return this.$validation[elementName]?.setError(params);
|
||||
},
|
||||
|
||||
setOptions: (options: Array<BaseOption<Values.ElementsTypes[E]>>) => {
|
||||
this.$options.setOptions(elementName, options);
|
||||
|
||||
@ -98,20 +108,5 @@ export default class CalculationStore {
|
||||
|
||||
return this.element(elementName);
|
||||
},
|
||||
|
||||
validate: ({ invalid, message, silent, helper }: ValidationParams) => {
|
||||
if (!this.$validation[elementName]) this.createElementValidation(elementName);
|
||||
|
||||
let removeError: RemoveError | undefined;
|
||||
|
||||
if (invalid) {
|
||||
removeError = this.$validation[elementName]?.addError(message, silent);
|
||||
if (helper && removeError) helper.add(removeError);
|
||||
} else {
|
||||
this.$validation[elementName]?.removeError(message);
|
||||
}
|
||||
|
||||
return removeError;
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@ -2,9 +2,8 @@ import { useStore } from '@/stores/hooks';
|
||||
|
||||
export function useValidation(elementName) {
|
||||
const { $calculation } = useStore();
|
||||
const messages = $calculation.$validation[elementName]?.getMessages();
|
||||
|
||||
if (messages?.length) {
|
||||
const hasErrors = $calculation.$validation?.[elementName]?.hasErrors;
|
||||
if (hasErrors) {
|
||||
return {
|
||||
help: 'Некорректные данные',
|
||||
isValid: false,
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import Validation from '../../validation';
|
||||
import type { RemoveError, ValidationParams } from '../../validation/types';
|
||||
import type { ValidationParams } from '../../validation/types';
|
||||
import type * as FinGAP from '@/Components/Calculation/Form/Insurance/FinGAPTable/types';
|
||||
import type RootStore from '@/stores/root';
|
||||
import type { IObservableArray } from 'mobx';
|
||||
@ -38,17 +38,10 @@ export default class FinGAPTable {
|
||||
.reduce((sum, risk) => sum + risk.premium, 0);
|
||||
}
|
||||
|
||||
public validate = ({ invalid, message, helper }: ValidationParams) => {
|
||||
let removeError: RemoveError | undefined;
|
||||
public setError = (params: ValidationParams) => this.validation.setError(params);
|
||||
|
||||
if (invalid) {
|
||||
removeError = this.validation?.addError(message);
|
||||
if (helper && removeError) helper.add(removeError);
|
||||
} else {
|
||||
this.validation?.removeError(message);
|
||||
}
|
||||
|
||||
return removeError;
|
||||
public removeError = (params: Pick<ValidationParams, 'key'>) => {
|
||||
this.validation.removeError(params);
|
||||
};
|
||||
|
||||
public clear = () => {
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import Validation from '../../validation';
|
||||
import type { RemoveError, ValidationParams } from '../../validation/types';
|
||||
import type { ValidationParams } from '../../validation/types';
|
||||
import type * as Insurance from '@/Components/Calculation/Form/Insurance/InsuranceTable/types';
|
||||
import * as insuranceTableConfig from '@/config/tables/insurance-table';
|
||||
import type RootStore from '@/stores/root';
|
||||
@ -40,17 +40,10 @@ export default class InsuranceTable {
|
||||
if (initialStatuses) this.statuses = initialStatuses;
|
||||
};
|
||||
|
||||
public validate = ({ invalid, message, helper }: ValidationParams) => {
|
||||
let removeError: RemoveError | undefined;
|
||||
public setError = (params: ValidationParams) => this.validation.setError(params);
|
||||
|
||||
if (invalid) {
|
||||
removeError = this.validation?.addError(message);
|
||||
if (helper && removeError) helper.add(removeError);
|
||||
} else {
|
||||
this.validation?.removeError(message);
|
||||
}
|
||||
|
||||
return removeError;
|
||||
public removeError = (params: Pick<ValidationParams, 'key'>) => {
|
||||
this.validation.removeError(params);
|
||||
};
|
||||
|
||||
public reset = () => {
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import Validation from '../../validation';
|
||||
import type { RemoveError, ValidationParams } from '../../validation/types';
|
||||
import type { ValidationParams } from '../../validation/types';
|
||||
import type { Row } from './types';
|
||||
import type RootStore from '@/stores/root';
|
||||
import type { IObservableArray } from 'mobx';
|
||||
@ -72,17 +72,10 @@ export default class PaymentsTable {
|
||||
this.setStatuses(statuses);
|
||||
};
|
||||
|
||||
public validate = ({ invalid, message, helper }: ValidationParams) => {
|
||||
let removeError: RemoveError | undefined;
|
||||
public setError = (params: ValidationParams) => this.validation.setError(params);
|
||||
|
||||
if (invalid) {
|
||||
removeError = this.validation?.addError(message);
|
||||
if (helper && removeError) helper.add(removeError);
|
||||
} else {
|
||||
this.validation?.removeError(message);
|
||||
}
|
||||
|
||||
return removeError;
|
||||
public removeError = (params: Pick<ValidationParams, 'key'>) => {
|
||||
this.validation.removeError(params);
|
||||
};
|
||||
|
||||
public reset = () => {
|
||||
|
||||
@ -1,32 +1,38 @@
|
||||
import type { RemoveError, ValidationConfig } from './types';
|
||||
import type { RemoveError, ValidationConfig, ValidationError, ValidationParams } from './types';
|
||||
import { makeAutoObservable } from 'mobx';
|
||||
import notification from 'ui/elements/notification';
|
||||
|
||||
export default class Validation {
|
||||
private params: ValidationConfig;
|
||||
private messages: Set<string>;
|
||||
private errors: Set<ValidationError>;
|
||||
|
||||
constructor(config: ValidationConfig) {
|
||||
this.params = config;
|
||||
this.messages = new Set();
|
||||
this.errors = new Set();
|
||||
makeAutoObservable(this);
|
||||
}
|
||||
|
||||
public get hasErrors() {
|
||||
return this.messages.size > 0;
|
||||
return this.errors.size > 0;
|
||||
}
|
||||
|
||||
public getMessages() {
|
||||
return [...this.messages];
|
||||
public getErrors() {
|
||||
return [...this.errors];
|
||||
}
|
||||
|
||||
public removeError = (message: string) => {
|
||||
this.messages.delete(message);
|
||||
if (this.messages.size === 0) notification.close(this.params.err_key);
|
||||
public removeError = ({ key }: Pick<ValidationError, 'key'>) => {
|
||||
const error = [...this.errors].find((x) => x.key === key);
|
||||
if (error) this.errors.delete(error);
|
||||
if (this.errors.size === 0) notification.close(this.params.err_key);
|
||||
};
|
||||
|
||||
public addError = (message: string, silent?: boolean) => {
|
||||
if (!silent && !this.messages.has(message)) {
|
||||
public setError = ({ key, message, silent }: ValidationParams) => {
|
||||
const error = [...this.errors].find((x) => x.key === key);
|
||||
if (error) this.removeError({ key });
|
||||
|
||||
this.errors.add({ key, message });
|
||||
|
||||
if (!silent) {
|
||||
notification.error({
|
||||
description: message,
|
||||
key: this.params.err_key,
|
||||
@ -34,13 +40,11 @@ export default class Validation {
|
||||
});
|
||||
}
|
||||
|
||||
this.messages.add(message);
|
||||
|
||||
return (() => this.removeError(message)) as RemoveError;
|
||||
return (() => this.removeError({ key })) as RemoveError;
|
||||
};
|
||||
|
||||
public clearErrors = () => {
|
||||
this.messages.clear();
|
||||
this.errors.clear();
|
||||
notification.close(this.params.err_key);
|
||||
};
|
||||
}
|
||||
|
||||
@ -1,15 +1,10 @@
|
||||
import type ValidationHelper from './helper';
|
||||
|
||||
export type ValidationConfig = {
|
||||
err_key: string;
|
||||
err_title: string;
|
||||
};
|
||||
|
||||
export type ValidationParams = {
|
||||
helper?: ValidationHelper;
|
||||
invalid: boolean;
|
||||
message: string;
|
||||
silent?: boolean;
|
||||
};
|
||||
export type ValidationError = { key: string; message: string };
|
||||
|
||||
export type ValidationParams = ValidationError & { silent?: boolean };
|
||||
|
||||
export type RemoveError = () => void;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user