spelling-style-fix #92
@ -33,13 +33,25 @@ const bookingLessBalanceByColumn = new Map(
|
||||
]),
|
||||
);
|
||||
|
||||
//Бронь <= Остаток после брони для листов КВП и Опер 4 формы
|
||||
const bookingLessBalance4From = [
|
||||
["data.q4.booking", "data.q4.residual_after_booking"],
|
||||
];
|
||||
|
||||
const bookingLessBalance4FormByColumn = new Map(
|
||||
bookingLessBalance.flatMap(([bookingKey, balanceKey]) => [
|
||||
[bookingKey, { bookingKey, balanceKey }],
|
||||
[balanceKey, { bookingKey, balanceKey }],
|
||||
]),
|
||||
);
|
||||
|
||||
//Перенос в фонд экономии <= Всего
|
||||
const transferLessTotal = [
|
||||
["data.q1.transfer_econ", "data.q1.total"],
|
||||
["data.q2.transfer_econ", "data.q2.total"],
|
||||
["data.q3.transfer_econ", "data.q3.total"],
|
||||
["data.q4.transfer_econ", "data.q4.total"],
|
||||
]
|
||||
];
|
||||
const transferLessTotalByColumn = new Map(
|
||||
transferLessTotal.flatMap(([transferKey, totalKey]) => [
|
||||
[transferKey, { transferKey: transferKey, totalKey: totalKey }],
|
||||
@ -47,6 +59,15 @@ const transferLessTotalByColumn = new Map(
|
||||
]),
|
||||
);
|
||||
|
||||
// Область действия всех правил. Пары можно добавлять, удалять и изменять.
|
||||
const bookingLessBalanceRuleScope = {
|
||||
includedScopes: [],
|
||||
excludedScopes: [
|
||||
{ formType: "FORM_4", sheetName: "AHR" },
|
||||
{ formType: "FORM_4", sheetName: "OPER" },
|
||||
],
|
||||
};
|
||||
|
||||
export const validationRules = [
|
||||
{
|
||||
id: "remaining-booking",
|
||||
@ -72,6 +93,7 @@ export const validationRules = [
|
||||
Number(value) !== 0,
|
||||
},
|
||||
{
|
||||
...bookingLessBalanceRuleScope,
|
||||
id: "booking-less-balance",
|
||||
columnKeys: bookingLessBalance.flat(),
|
||||
errorColumnKeys: bookingLessBalance.map(([bookingKey]) => bookingKey),
|
||||
@ -80,8 +102,41 @@ export const validationRules = [
|
||||
const pair = bookingLessBalanceByColumn.get(columnKey);
|
||||
if (!pair) return false;
|
||||
|
||||
const booking = pair.bookingKey.split(".").reduce((value, key) => value?.[key], row);
|
||||
const balance = pair.balanceKey.split(".").reduce((value, key) => value?.[key], row);
|
||||
const booking = pair.bookingKey
|
||||
.split(".")
|
||||
.reduce((value, key) => value?.[key], row);
|
||||
const balance = pair.balanceKey
|
||||
.split(".")
|
||||
.reduce((value, key) => value?.[key], row);
|
||||
|
||||
return (
|
||||
booking !== null &&
|
||||
booking !== undefined &&
|
||||
booking !== "" &&
|
||||
balance !== null &&
|
||||
balance !== undefined &&
|
||||
balance !== "" &&
|
||||
Number.isFinite(Number(booking)) &&
|
||||
Number.isFinite(Number(balance)) &&
|
||||
Number(booking) > Number(balance)
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "booking-less-balance-4-form",
|
||||
columnKeys: bookingLessBalance4From.flat(),
|
||||
errorColumnKeys: bookingLessBalance4From.map(([bookingKey]) => bookingKey),
|
||||
message: "Сумма бронирования превышает остаток после бронирования",
|
||||
isInvalid: (_value, row, columnKey) => {
|
||||
const pair = bookingLessBalance4FormByColumn.get(columnKey);
|
||||
if (!pair) return false;
|
||||
|
||||
const booking = pair.bookingKey
|
||||
.split(".")
|
||||
.reduce((value, key) => value?.[key], row);
|
||||
const balance = pair.balanceKey
|
||||
.split(".")
|
||||
.reduce((value, key) => value?.[key], row);
|
||||
|
||||
return (
|
||||
booking !== null &&
|
||||
@ -105,8 +160,12 @@ export const validationRules = [
|
||||
const pair = transferLessTotalByColumn.get(columnKey);
|
||||
if (!pair) return false;
|
||||
|
||||
const transfer = pair.transferKey.split(".").reduce((value, key) => value?.[key], row);
|
||||
const total = pair.totalKey.split(".").reduce((value, key) => value?.[key], row);
|
||||
const transfer = pair.transferKey
|
||||
.split(".")
|
||||
.reduce((value, key) => value?.[key], row);
|
||||
const total = pair.totalKey
|
||||
.split(".")
|
||||
.reduce((value, key) => value?.[key], row);
|
||||
|
||||
return (
|
||||
transfer !== null &&
|
||||
|
||||
@ -1,30 +1,34 @@
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import { validationRules } from '../constants/validation';
|
||||
import { buildColumnHeaderPaths, collectInvalidErrors } from '../utils/validationUtils';
|
||||
import { buildColumnHeaderPaths, collectInvalidErrors, isValidationRuleEnabled } from '../utils/validationUtils';
|
||||
|
||||
export const useValidationRules = (data = [], columns = []) => {
|
||||
export const useValidationRules = (data = [], columns = [], formType, sheetName) => {
|
||||
const headerPaths = useMemo(() => buildColumnHeaderPaths(columns), [columns]);
|
||||
const activeRules = useMemo(
|
||||
() => validationRules.filter((rule) => isValidationRuleEnabled(rule, { formType, sheetName })),
|
||||
[formType, sheetName],
|
||||
);
|
||||
|
||||
const errors = useMemo(() => {
|
||||
const result = [];
|
||||
const seen = new Set();
|
||||
|
||||
for (const rule of validationRules) {
|
||||
for (const rule of activeRules) {
|
||||
collectInvalidErrors(data, rule, headerPaths, result, seen);
|
||||
}
|
||||
|
||||
return result;
|
||||
}, [data, headerPaths]);
|
||||
}, [activeRules, data, headerPaths]);
|
||||
|
||||
const isCellInvalid = useCallback(
|
||||
(columnId, value, row) =>
|
||||
validationRules.some(
|
||||
activeRules.some(
|
||||
(rule) =>
|
||||
rule.columnKeys.includes(columnId) &&
|
||||
(!rule.appliesToRow || rule.appliesToRow(row)) &&
|
||||
rule.isInvalid(value, row, columnId),
|
||||
),
|
||||
[],
|
||||
[activeRules],
|
||||
);
|
||||
|
||||
return { errors, isCellInvalid };
|
||||
|
||||
@ -1,6 +1,21 @@
|
||||
// Достаёт вложенное поле из объекта строки таблицы
|
||||
export const getValueByPath = (row, path) => path.split('.').reduce((value, key) => value?.[key], row);
|
||||
|
||||
const matchesValidationScope = (scope, { formType, sheetName }) =>
|
||||
(!scope.formType || scope.formType === formType) &&
|
||||
(!scope.sheetName || scope.sheetName === sheetName);
|
||||
|
||||
// Пустой includedScopes означает «работает везде»; исключения приоритетнее.
|
||||
export const isValidationRuleEnabled = (rule, context = {}) => {
|
||||
const includedScopes = rule.includedScopes || [];
|
||||
const excludedScopes = rule.excludedScopes || [];
|
||||
|
||||
if (excludedScopes.some((scope) => matchesValidationScope(scope, context))) return false;
|
||||
if (includedScopes.length && !includedScopes.some((scope) => matchesValidationScope(scope, context))) return false;
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
// Строит пути заголовков колонок
|
||||
export const buildColumnHeaderPaths = (columns = [], path = [], map = {}) => {
|
||||
for (const column of columns) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user