From 5e106e7bab2bfc1597380d2c26f12e9cc07c6e54 Mon Sep 17 00:00:00 2001 From: PotapovaA Date: Wed, 12 Aug 2026 16:03:50 +0300 Subject: [PATCH] add validation --- .../RealtimeTable/constants/validation.js | 79 +++++++++++++++++++ .../RealtimeTable/hooks/useValidationRules.js | 5 +- .../RealtimeTable/utils/validationUtils.js | 4 +- 3 files changed, 85 insertions(+), 3 deletions(-) diff --git a/web/src/components/RealtimeTable/constants/validation.js b/web/src/components/RealtimeTable/constants/validation.js index c49fc5b..d8eec05 100644 --- a/web/src/components/RealtimeTable/constants/validation.js +++ b/web/src/components/RealtimeTable/constants/validation.js @@ -18,6 +18,35 @@ const zeroValueRequirementKeys = [ "data.q4.adj_current", ]; +//Бронь <= Остаток после брони +const bookingLessBalance = [ + ["data.q1.booking", "data.q1.residual_after_booking"], + ["data.q2.booking", "data.q2.residual_after_booking"], + ["data.q3.booking", "data.q3.residual_after_booking"], + ["data.q4.booking", "data.q4.residual_after_booking"], +]; + +const bookingLessBalanceByColumn = 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 }], + [totalKey, { transferKey: transferKey, totalKey: totalKey }], + ]), +); + export const validationRules = [ { id: "remaining-booking", @@ -42,4 +71,54 @@ export const validationRules = [ Number.isFinite(Number(value)) && Number(value) !== 0, }, + { + id: "booking-less-balance", + columnKeys: bookingLessBalance.flat(), + errorColumnKeys: bookingLessBalance.map(([bookingKey]) => bookingKey), + message: "Сумма бронирования превышает остаток после бронирования", + isInvalid: (_value, row, columnKey) => { + 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); + + return ( + booking !== null && + booking !== undefined && + booking !== "" && + balance !== null && + balance !== undefined && + balance !== "" && + Number.isFinite(Number(booking)) && + Number.isFinite(Number(balance)) && + Number(booking) > Number(balance) + ); + }, + }, + { + id: "transfer-less-total", + columnKeys: transferLessTotal.flat(), + errorColumnKeys: transferLessTotal.map(([transferKey]) => transferKey), + message: "Перенос в фонд экономии превышает всего", + isInvalid: (_value, row, columnKey) => { + 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); + + return ( + transfer !== null && + transfer !== undefined && + transfer !== "" && + total !== null && + total !== undefined && + total !== "" && + Number.isFinite(Number(transfer)) && + Number.isFinite(Number(total)) && + Number(transfer) > Number(total) + ); + }, + }, ]; diff --git a/web/src/components/RealtimeTable/hooks/useValidationRules.js b/web/src/components/RealtimeTable/hooks/useValidationRules.js index 697eecb..4007e47 100644 --- a/web/src/components/RealtimeTable/hooks/useValidationRules.js +++ b/web/src/components/RealtimeTable/hooks/useValidationRules.js @@ -19,7 +19,10 @@ export const useValidationRules = (data = [], columns = []) => { const isCellInvalid = useCallback( (columnId, value, row) => validationRules.some( - (rule) => rule.columnKeys.includes(columnId) && (!rule.appliesToRow || rule.appliesToRow(row)) && rule.isInvalid(value), + (rule) => + rule.columnKeys.includes(columnId) && + (!rule.appliesToRow || rule.appliesToRow(row)) && + rule.isInvalid(value, row, columnId), ), [], ); diff --git a/web/src/components/RealtimeTable/utils/validationUtils.js b/web/src/components/RealtimeTable/utils/validationUtils.js index 6eccea6..3d12579 100644 --- a/web/src/components/RealtimeTable/utils/validationUtils.js +++ b/web/src/components/RealtimeTable/utils/validationUtils.js @@ -20,8 +20,8 @@ export const buildColumnHeaderPaths = (columns = [], path = [], map = {}) => { export const collectInvalidErrors = (rows, rule, headerPaths, errors, seen) => { for (const row of rows) { if (!rule.appliesToRow || rule.appliesToRow(row)) { - for (const columnKey of rule.columnKeys) { - if (!rule.isInvalid(getValueByPath(row, columnKey))) continue; + for (const columnKey of rule.errorColumnKeys || rule.columnKeys) { + if (!rule.isInvalid(getValueByPath(row, columnKey), row, columnKey)) continue; const errorId = `${rule.id}:${columnKey}`; if (seen.has(errorId)) continue;