arm-core/web/src/widgets/DocumentReview/statementGrid.test.ts
Andrey Epifancev d5118ba3a6 fix(web): перенос правки шаблонных строк во встроенный фронт (#80)
Правка arm-web 9e89b9f перенесена в arm-core/web: id и origin у строк и ячеек
формы, ключ достроенной строки code-<код>, отправка line_code, раскладка
formLayout. Без неё встроенный фронт отправлял бы line_id "null" (I-1, R-12).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-09 12:33:00 +04:00

110 lines
4.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { describe, expect, it } from 'vitest';
import type { StatementOut, ValueOut } from '../../types/api';
import { applyDraftInput, buildStatementGrid, cellFlags, cellKey, draftToPatch } from './statementGrid';
const v = (id: string | null, column_id: string, column_index: number, extra: Partial<ValueOut> = {}): ValueOut => ({
id,
column_id,
column_index,
amount: '1.00',
string_value: null,
confidence: '100',
is_recognized: true,
is_modified: false,
original_amount: null,
...extra,
});
const statement = {
columns: [
{ id: 'c2', column_index: 1, label: '31.12.2023', period_date: null, is_current: false },
{ id: 'c1', column_index: 0, label: '30.09.2024', period_date: null, is_current: true },
],
lines: [{ id: 'l1', origin: 'source', line_code: '1600', line_name: 'Баланс', values: [v('v2', 'c2', 1), v('v1', 'c1', 0)] }],
} as unknown as StatementOut;
describe('buildStatementGrid', () => {
it('колонки по column_index, ячейки по column_id', () => {
const grid = buildStatementGrid(statement);
expect(grid.columns.map((c) => c.id)).toEqual(['c1', 'c2']);
expect(grid.rows[0]).toMatchObject({ key: 'l1', lineId: 'l1', code: '1600', name: 'Баланс', origin: 'source' });
expect(grid.rows[0].cells.c1?.id).toBe('v1');
expect(grid.rows[0].cells.c2?.id).toBe('v2');
});
it('достроенная строка: ключ по коду, lineId = null', () => {
const withTemplate = {
...statement,
lines: [
...statement.lines,
{
id: null,
origin: 'template',
line_code: '1700',
line_name: 'БАЛАНС',
values: [v(null, 'c1', 0, { amount: null, confidence: null, is_recognized: null })],
},
],
} as unknown as StatementOut;
const row = buildStatementGrid(withTemplate).rows[1];
expect(row).toMatchObject({ key: 'code-1700', lineId: null, code: '1700', origin: 'template' });
});
});
describe('cellFlags', () => {
it('внимание: уверенность < 100 или не распознано', () => {
expect(cellFlags(v('a', 'c', 0, { confidence: '87.5' })).attention).toBe(true);
expect(cellFlags(v('a', 'c', 0, { is_recognized: false })).attention).toBe(true);
expect(cellFlags(v('a', 'c', 0)).attention).toBe(false);
expect(cellFlags(v('a', 'c', 0, { confidence: null })).attention).toBe(false);
});
it('пустая достроенная ячейка не требует внимания', () => {
expect(cellFlags(v(null, 'c', 0, { amount: null, confidence: null, is_recognized: null }))).toEqual({
attention: false,
modified: false,
});
});
it('исправлено', () => {
expect(cellFlags(v('a', 'c', 0, { is_modified: true })).modified).toBe(true);
expect(cellFlags(undefined)).toEqual({ attention: false, modified: false });
});
});
describe('draftToPatch', () => {
const rows = buildStatementGrid({
...statement,
lines: [...statement.lines, { id: null, origin: 'template', line_code: '1700', line_name: 'БАЛАНС', values: [] }],
} as unknown as StatementOut).rows;
it('строка из БД — line_id, достроенная — line_code', () => {
expect(draftToPatch({ [cellKey('l1', 'c1')]: '10.5', [cellKey('code-1700', 'c1')]: null }, rows)).toEqual([
{ line_id: 'l1', column_id: 'c1', amount: '10.5' },
{ line_code: '1700', column_id: 'c1', amount: null },
]);
});
it('неизвестная строка — ошибка, а не тихий пропуск', () => {
expect(() => draftToPatch({ [cellKey('ghost', 'c1')]: '1' }, rows)).toThrow();
});
});
describe('applyDraftInput', () => {
const parse = (s: string): { ok: true; value: string | null } | { ok: false } => {
const n = s.replace(/\s/g, '').replace(',', '.');
if (n === '') {
return { ok: true, value: null };
}
return /^-?\d+(\.\d+)?$/.test(n) ? { ok: true, value: n } : { ok: false };
};
it('изменение попадает в черновик', () => {
expect(applyDraftInput({}, 'k', '5', '1.00', parse)).toEqual({ k: '5' });
});
it('возврат к исходному убирает ключ (с учётом нулей дроби)', () => {
expect(applyDraftInput({ k: '5' }, 'k', '1', '1.00', parse)).toEqual({});
expect(applyDraftInput({ k: '5' }, 'k', '1,0', '1.00', parse)).toEqual({});
});
it('пустое при пустом исходном — не изменение', () => {
expect(applyDraftInput({ k: '5' }, 'k', '', null, parse)).toEqual({});
});
it('неверный формат остаётся в черновике', () => {
expect(applyDraftInput({}, 'k', '12,3,4', '1.00', parse)).toEqual({ k: '12,3,4' });
});
});