import { getDataRowId } from './rowUtils.js'; // Достаёт вложенное поле из объекта строки таблицы 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) { const nextPath = column.header ? [...path, column.header] : path; if (column.columns?.length) { buildColumnHeaderPaths(column.columns, nextPath, map); } else if (column.accessorKey) { map[column.accessorKey] = nextPath; } } return map; }; // AURORA-1893: человекочитаемая подпись строки для меню плашки ошибок — // название статьи/ВСП, если есть, иначе номер строки (колонка «№»). const getRowLabel = (row) => { const data = row?.data; if (data?.header?.name) return data.header.name; if (data?.name) return data.name; if (row?.sort_order !== undefined && row?.sort_order !== null) return String(row.sort_order); return String(getDataRowId(data) ?? ''); }; // Обходит строки и собирает ошибки по правилу валидации — по записи на каждую // ошибочную ЯЧЕЙКУ (не одна запись на колонку, как было раньше), чтобы плашка // могла показать счётчик и перейти к конкретной строке. export const collectInvalidErrors = (rows, rule, errors, seen) => { for (const row of rows) { if (!rule.appliesToRow || rule.appliesToRow(row)) { for (const columnKey of rule.errorColumnKeys || rule.columnKeys) { if (!rule.isInvalid(getValueByPath(row, columnKey), row, columnKey)) continue; const lineId = getDataRowId(row.data); const errorId = `${rule.id}:${columnKey}:${lineId}`; if (seen.has(errorId)) continue; seen.add(errorId); errors.push({ id: errorId, ruleId: rule.id, columnId: columnKey, lineId, rowLabel: getRowLabel(row), message: rule.message, }); } } collectInvalidErrors(row.subRows || [], rule, errors, seen); } }; // AURORA-1893: группирует ошибки по «правило + колонка» (как раньше отображалось // в плашке) и прикладывает к каждой группе список конкретных ячеек со счётчиком. export const groupValidationErrors = (errors) => { const groupsByKey = new Map(); for (const error of errors) { const key = `${error.ruleId}:${error.columnId}`; let group = groupsByKey.get(key); if (!group) { group = { id: key, ruleId: error.ruleId, columnId: error.columnId, message: error.message, cells: [], count: 0, }; groupsByKey.set(key, group); } group.cells.push(error); group.count += 1; } return [...groupsByKey.values()]; };