import { describe, expect, it } from 'vitest'; import type { EntryOut, OsvGroupType } from '../../types/api'; import { flattenEntries } from './osvRows'; let row = 0; const e = ( id: string, parent: string | null, groupType: OsvGroupType, value: string, amounts: Partial> = {}, extra: Partial = {}, ): EntryOut => ({ id, row_number: ++row, level: null, parent_entry_id: parent, row_value: value, group_type: groupType, group_name: null, account_code: null, counterparty_name: null, counterparty_inn: null, opening_debit: null, opening_credit: null, turnover_debit: null, turnover_credit: null, closing_debit: null, closing_credit: null, ...amounts, ...extra, }); describe('flattenEntries', () => { it('строки — только аналитика нижнего уровня с субсчётом', () => { const { rows } = flattenEntries([ e('62', null, 'ACCOUNT', '62', { opening_debit: '10' }, { account_code: '62' }), e('62.01', '62', 'ACCOUNT', '62.01', {}, { account_code: '62.01' }), e('a', '62.01', 'COUNTERPARTY', 'ООО А', { opening_debit: '7' }, { account_code: '62.01', counterparty_inn: '123' }), e('b', '62.01', 'COUNTERPARTY', 'ООО Б', { opening_debit: '3' }, { account_code: '62.01' }), ]); expect(rows.map((r) => [r.account, r.analytics, r.inn, r.opening_debit])).toEqual([ ['62.01', 'ООО А', '123', '7'], ['62.01', 'ООО Б', null, '3'], ]); }); it('подразделение — из родительской строки DEPARTMENT', () => { const { rows, hasDepartments } = flattenEntries([ e('62', null, 'ACCOUNT', '62', {}, { account_code: '62' }), e('62.01', '62', 'ACCOUNT', '62.01', {}, { account_code: '62.01' }), e('d', '62.01', 'DEPARTMENT', 'БП Аренда', {}, { account_code: '62.01' }), e('a', 'd', 'COUNTERPARTY', 'РБ', {}, { account_code: '62.01' }), ]); expect(hasDepartments).toBe(true); expect(rows.map((r) => [r.department, r.analytics])).toEqual([['БП Аренда', 'РБ']]); }); it('договоры не отдельными строками: остаётся строка контрагента', () => { const { rows } = flattenEntries([ e('58', null, 'ACCOUNT', '58.03', {}, { account_code: '58.03' }), e('a', '58', 'COUNTERPARTY', 'РБ ТК', { opening_debit: '100' }, { account_code: '58.03' }), e('c1', 'a', 'CONTRACT', 'Договор 1', { opening_debit: '60' }, { account_code: '58.03' }), e('c2', 'a', 'CONTRACT', 'Договор 2', { opening_debit: '40' }, { account_code: '58.03' }), ]); expect(rows.map((r) => [r.analytics, r.opening_debit])).toEqual([['РБ ТК', '100']]); }); it('объекты ОС (OTHER) — строки аналитики', () => { const { rows } = flattenEntries([ e('01', null, 'ACCOUNT', '01', {}, { account_code: '01' }), e('o', '01', 'OTHER', 'Автопогрузчик', { opening_debit: '5' }, { account_code: '01.01' }), ]); expect(rows.map((r) => [r.account, r.analytics])).toEqual([['01.01', 'Автопогрузчик']]); }); it('субсчёт и подразделение без нижних строк — строка с пустой аналитикой; пустые без сумм — нет', () => { const { rows } = flattenEntries([ e('62', null, 'ACCOUNT', '62', {}, { account_code: '62' }), e('62.21', '62', 'ACCOUNT', '62.21', { closing_credit: '128041.39' }, { account_code: '62.21' }), e('62.22', '62', 'ACCOUNT', '62.22', {}, { account_code: '62.22' }), e('d', '62', 'DEPARTMENT', 'Опт', { turnover_debit: '1' }, { account_code: '62' }), ]); expect(rows.map((r) => [r.account, r.department, r.analytics])).toEqual([ ['62.21', null, null], ['62', 'Опт', null], ]); }); it('итог — сумма корневых строк, а не листьев (развёрнутое сальдо 1С)', () => { const { total } = flattenEntries([ e('60', null, 'ACCOUNT', '60', { closing_credit: '100.10' }, { account_code: '60' }), e('a', '60', 'COUNTERPARTY', 'А', { closing_credit: '150.10' }, { account_code: '60' }), e('76', null, 'ACCOUNT', '76', { closing_credit: '0.20' }, { account_code: '76' }), ]); expect(total.closing_credit).toBe('100.30'); expect(total.opening_debit).toBeNull(); }); it('субсчета для фильтра — уникальные, в натуральном порядке', () => { const { accounts } = flattenEntries([ e('62', null, 'ACCOUNT', '62', {}, { account_code: '62' }), e('b', '62', 'COUNTERPARTY', 'Б', {}, { account_code: '62.10' }), e('a', '62', 'COUNTERPARTY', 'А', {}, { account_code: '62.02' }), e('c', '62', 'COUNTERPARTY', 'В', {}, { account_code: '62.02' }), ]); expect(accounts).toEqual(['62.02', '62.10']); }); it('пустой документ', () => { expect(flattenEntries([])).toEqual({ rows: [], total: expect.objectContaining({ opening_debit: null }), accounts: [], hasDepartments: false, }); }); });