import { describe, expect, it } from 'vitest'; import { formLayout } from './formLayout'; import type { GridRow } from './statementGrid'; const row = (code: string | null, name: string | null = `строка ${code}`): GridRow => ({ key: code ?? 'x', lineId: code ?? 'x', origin: 'source', code, name, cells: {}, }); const shape = (rows: ReturnType) => rows.map((r) => (r.kind === 'section' ? `# ${r.title}` : `${r.row.code}${r.total ? ' (итог)' : ''}`)); describe('formLayout — бухгалтерский баланс (0710001)', () => { it('порядок формы: статьи раздела, затем итог; 1600 после раздела II; 1700 в конце', () => { const rows = ['1100', '1105', '1150', '1200', '1210', '1300', '1310', '1400', '1410', '1500', '1510', '1600', '1700'].map((c) => row(c), ); expect(shape(formLayout('BALANCE', rows))).toEqual([ '# АКТИВ', '# I. Внеоборотные активы', '1105', '1150', '1100 (итог)', '# II. Оборотные активы', '1210', '1200 (итог)', '1600 (итог)', '# ПАССИВ', '# III. Капитал и резервы', '1310', '1300 (итог)', '# IV. Долгосрочные обязательства', '1410', '1400 (итог)', '# V. Краткосрочные обязательства', '1510', '1500 (итог)', '1700 (итог)', ]); }); it('пустой раздел не выводится; строки вне формы — в конце под «Прочие строки»', () => { const rows = [row('1150'), row('9999'), row(null, 'без кода')]; expect(shape(formLayout('BALANCE', rows))).toEqual(['# АКТИВ', '# I. Внеоборотные активы', '1150', '# Прочие строки', '9999', 'null']); }); it('срезает приклеенные OCR заголовки разделов из названия, полное — в fullName', () => { const [, , line] = formLayout('BALANCE', [row('1105', 'АКТИВ I. Внеоборотные активы Гудвил')]); expect(line.kind === 'line' && line.displayName).toBe('Гудвил'); expect(line.kind === 'line' && line.fullName).toBe('АКТИВ I. Внеоборотные активы Гудвил'); }); it('если после среза ничего не осталось — название не трогаем', () => { const [, , line] = formLayout('BALANCE', [row('1110', 'I. Внеоборотные активы')]); expect(line.kind === 'line' && line.displayName).toBe('I. Внеоборотные активы'); expect(line.kind === 'line' && line.fullName).toBeNull(); }); }); describe('formLayout — ОФР (0710002)', () => { it('итог XX00 — после статей своей сотни, без заголовков разделов', () => { const rows = ['2100', '2110', '2120', '2200', '2210', '2300', '2310', '2400', '2410'].map((c) => row(c)); expect(shape(formLayout('INCOME', rows))).toEqual([ '2110', '2120', '2100 (итог)', '2210', '2200 (итог)', '2310', '2300 (итог)', '2410', '2400 (итог)', ]); }); }); describe('formLayout — ОФР, справочные строки', () => { it('2900 «Базовая прибыль на акцию» — не итог и стоит перед 2910', () => { expect( shape( formLayout( 'INCOME', ['2910', '2900', '2400', '2410'].map((c) => row(c)), ), ), ).toEqual(['2410', '2400 (итог)', '2900', '2910']); }); }); describe('formLayout — строки «БАЛАНС»', () => { it('1600 и 1700 у баланса помечены grand, остальные итоги — нет', () => { const layout = formLayout( 'BALANCE', ['1100', '1150', '1600', '1300', '1310', '1700'].map((c) => row(c)), ); const grand = layout.filter((r) => r.kind === 'line' && r.grand).map((r) => (r.kind === 'line' ? r.row.code : '')); expect(grand).toEqual(['1600', '1700']); }); it('у ОФР grand нет', () => { const layout = formLayout( 'INCOME', ['2110', '2100', '2400'].map((c) => row(c)), ); expect(layout.some((r) => r.kind === 'line' && r.grand)).toBe(false); }); });