18 KiB
18 KiB
code, title
| code | title |
|---|---|
| DOC-037740 | FNR01 Модуль загрузки документов и создания записи |
FNR01 Модуль загрузки документов и создания записи
ОСВ
Алгоритм работы:
- Пользователь авторизуется в системе
- Система отрисовывает списочную форму сформированных консолидированных отчетов. Пользователь нажимает "Сформировать новый отчет"
- Система отрисовывает модальное окно для выбора ГСК -> указания отчетного периода -> выбора компании -> добавления документов по компании
- Пользователь нажимает "Добавить" и выбирает документы для загрузки (допустимые форматы файлов xls, xlsx).
- Для каждого загружаемого файла ОСВ система создает запись в таблице stored\_file
- После создания записи stored\_file система создает задание на распознавание в таблице recognition\_job.
- Сервис выполняет парсинг файла ОСВ. Для каждой распознанной расчетной строки система создает отдельную запись в таблице osv\_entry.
- После получения account\_code система выполняет нормализацию счета и поиск соответствующей записи в справочнике chart\_of\_account.
- в osv\_entry.account\_id записывается chart\_of\_account.id.
- в osv\_entry.account\_root записывается корневой счет.
- При наличии в строке контрагента система выполняет его сопоставление со справочником legal\_entity. Если найдено единственное соответствие, в osv\_entry.counterparty\_id записывается legal\_entity.id. Выполняется последовательный поиск по:
- counterparty\_inn
- legal\_entity.name
- legal\_entity\_alias.normalized\_alias
Маппинг stored\_file
| Поле stored\_file | Источник | Пример | Правило заполнения |
| id | Сгенерированный id | uuid | Генерируется до запуска парсинга |
| legal\_entity\_id | id владельца ОСВ | uuid организации | Идентификатор компании, для которой пользователь загрузил ОСВ |
| document\_type | Константа для данного сценария | ОСВ | Всегда "ОСВ" |
| file\_type | Расширение загруженного файла | XLSX | Для ОСВ используется тип XLSX, XLS |
| original\_name | Имя исходного файла | ОСВ\_ООО\_Пекарня\_6м2026.xlsx | Сохранить исходное имя файла |
| s3\_key | Хранилище | ключ объекта | Формируется при сохранении файла в хранилище |
| content\_hash | Содержимое файла | sha256 | Хеш файла для контроля дублей |
| size\_bytes | Размер файла | 12345 | Размер файла |
| period\_year | Шапка ОСВ | 2026 | Нормализованный год |
| period\_quarter | Шапка ОСВ | Q2 | Заполнить нормализованным периодом |
| status | Система | Обработка | При запуске парсинга - "Обработка" После успешного завершения - "Загружен" При ошибке - "Ошибка" При расхождении контрольных сумм - "Требуется проверка" |
| current\_recognition\_job\_id | Результат успешного запуска | uuid recognition\_job | После успешного завершения парсинга записать id recognition\_job |
| uploaded\_by | id пользователя | uuid | Пользователь, выполнивший загрузку |
| uploaded\_at | Системное время | timestamp | Дата и время загрузки |
Маппинг recognition\_job
| Поле recognition\_job | Источник | Пример | Правило заполнения |
| id | Сгенерированный id | uuid | Создать до передачи файла в сервис парсинга |
| file\_id | id загруженного файла | uuid файла | stored\_file.id на обрабатываемый файл |
| pipeline | Тип файла | tabular | Для ОСВ tabular |
| service\_version | Версия сервиса парсинга | 1.0 | Заполняет сервис |
| status | Состояние обработки | queued / processing / done / error | Обновлять по жизненному циклу парсинга |
| attempt | Номер попытки | 1 | Увеличивать при повторном запуске |
| queue\_msg\_id | Сообщение очереди | id сообщения | Служебное поле дедупликации |
| requested\_at | Системное время | timestamp | Время постановки в обработку |
| started\_at | Системное время | timestamp | Время начала обработки |
| finished\_at | Системное время | timestamp | Время завершения обработки |
| error\_message | Ошибка сервиса | текст / NULL | Заполнять только при status = error |
| raw\_result\_s3\_key | Результат парсинга | ключ JSON | Ключ JSON результата |
c. Система создаёт запись в таблице **osv\\_entry** и заполняет данными:
Маппинг osv\_entry
| Поле | Источник в ОСВ | Пример | Тип | Правило заполнения |
| id | Сгенерированный id | - | uuid | Генерируется для каждой сохраняемой строки |
| stored\_file\_id | Сгенерированный id файла | stored\_file.id | uuid | Записать id файла, из которого получена строка |
| recognition\_job\_id | Сгенерированный id парсинга | recognition\_job.id | uuid | Записать id текущего запуска парсинга |
| legal\_entity\_id | id владельца ОСВ | stored\_file.legal\_entity\_id | uuid | Организация-владелец ОСВ |
| account\_code | Счет или унаследованный текущий счет | 62.01 | varchar | Сохранить исходный код счета |
| account\_id | Результат нормализации account\_code | id счета 62.01 | uuid / NULL | Найти точное соответствие account\_code = chart\_of\_account.code. Если точного счета нет, account\_id оставить NULL |
| account\_root | Нормализация account\_code до корневого счета | id счета 62.01 | varchar | Корневой счет первого уровня. 62.01.01 -> 62 |
| counterparty\_name | Контрагенты | ООО «Ромашка» | varchar / NULL | Сохранить исходное наименование после |
| counterparty\_inn | Поле ИНН контрагента | 1650123456 | varchar / NULL | Оставить только цифры. При пустом значении оставить NULL |
| counterparty\_id | Результат сопоставления | uuid / NULL | uuid / NULL | Сначала поиск по ИНН. Если не найдено - по нормализованному имени legal\_entity.name и legal\_entity\_alias.normalized\_alias. При отсутствии совпадения оставить NULL |
| opening\_debit | Сальдо на начало периода -> Дебет | 100000.00 | numeric / NULL | Перенести число из соответствующей ячейки |
| opening\_credit | Сальдо на начало периода -> Кредит | 0.00 | numeric / NULL | Перенести число из соответствующей ячейки |
| turnover\_debit | Обороты за период -> Дебет | 250000.00 | numeric / NULL | Перенести число из соответствующей ячейки |
| turnover\_credit | Обороты за период -> Кредит | 170000.00 | numeric / NULL | Перенести число из соответствующей ячейки |
| closing\_debit | Сальдо на конец периода -> Дебет | 180000.00 | numeric / NULL | Перенести число из соответствующей ячейки |
| closing\_credit | Сальдо на конец периода -> Кредит | 0.00 | numeric / NULL | Перенести число из соответствующей ячейки |
Маппинг отчетного периода
| Значение периода в исходной ОСВ | Возможные варианты написания | Значение в БД period\_quarter | Нарастающий итог (is\_cumulative) | Примечание |
|---|---|---|---|---|
| 1 квартал | 1 квартал, I квартал, 1 кв., I кв., за 1 квартал | 1 | true | |
| 3 месяца | 3 месяца, за 3 месяца, 3 мес., январь-март, 01.01-31.03 | 1 | true | |
| 2 квартал | 2 квартал, II квартал, 2 кв., II кв., за 2 квартал | 2 | false | Нужно уточнить, возможно ли такое, что в ОСВ указывается номер квартала, но считается все равно нарастающий итог |
| Полугодие | полугодие, 1 полугодие, I полугодие, за полугодие, за 1 полугодие | 2 | true | |
| 6 месяцев | 6 месяцев, за 6 месяцев, 6 мес., январь-июнь, 01.01-30.06 | 2 | true | |
| 3 квартал | 3 квартал, III квартал, 3 кв., III кв., за 3 квартал | 3 | false | Нужно уточнить, возможно ли такое, что в ОСВ указывается номер квартала, но считается все равно нарастающий итог |
| 9 месяцев | 9 месяцев, за 9 месяцев, 9 мес., январь-сентябрь, 01.01-30.09 | 3 | true | |
| 4 квартал | 4 квартал, IV квартал, 4 кв., IV кв., за 4 квартал | 4 | false | Нужно уточнить, возможно ли такое, что в ОСВ указывается номер квартала, но считается все равно нарастающий итог |
| Год | год, за год, за 2026 год, годовая, годовая отчетность | 4 | true | |
| 12 месяцев | 12 месяцев, за 12 месяцев, 12 мес., январь-декабрь, 01.01-31.12 | 4 | true |
Данные полученные в формате json от ИС Сканирование маппируются:
- в таблицу fin\_report\_header по следующему правилу:
| Источник (JSON путь) | Целевое поле | Логика преобразования / Примечания/Пример заполнения |
| data\_ocr[0].label.form\_name | report\_type | "Отчет о финансовых результатах"; "Бухгалтерский баланс" |
| data\_ocr[0].inn.inn | org\_id | Поиск организации по ИНН в справочнике получение org\_id |
| data\_ocr[0].organization.full\_name | org\_id | Сличение с наименованием в справочнике Organizations.full\_name. При несовпадении ищем совпадение в связанной таблице Organization\_aliases. alias\_name по ключу org.id. Если не найдено совпадение то выводим предупреждение. |
| data\_ocr[0].units.measurement | unit | в тыс рублей |
| - | currency | По умолчанию 'RUB', если валюта не указана в JSON |
| // ждем обновленный джейсон пока от ИС сканирование | reporting\_date | формат даты: DD.MM.YYYY |
| Внешний контекст | package\_id | ID пакета документов из внешней системы (не в JSON) |
| Системная генерация | report\_id | Автоинкремент / UUID при вставке |
| Системное время | created\_at | NOW() при загрузке документа |
- в таблицу F in\_report\_rows по следующему правилу:
| Источник (JSON путь) | Целевое поле | Логика преобразования / Примечания/Пример |
| data\_ocr[0].table\_structure.rows[\*] | (новая строка) | Итерация: одна запись в БД на каждую row из массива |
| rows[0].cells[1].value | наименование колонки | Строка нулевого индекса содержит значения для описания наименований колонок таблицы. Значения Наименование показателя и Код - константы, их можно не парсить каждый раз, но значения колонок периода потребуется переносить как есть полученное из json |
| rows[0].cells[2].value | наименование колонки | |
| rows[0].cells[3].value | наименование колонки | |
| rows[0].cells40].value | наименование колонки | |
| rows[\*].cells[0].value | row\_name | Наименование показателя (напр. Выручка, Основные средства) |
| rows[\*].cells[1].value | row\_code | Код строки: '2110', '1150', '2400' и т.д. |
| rows[\*].cells[2].value | value\_current | Значение за текущий период. Парсить число, заменять , п, м NULL, значение данное в скобках трактовать как отрицательное. Пример: было (2100000) - стало -21000000 |
| rows[\*].cells[3].value | value\_prior\_year | Значение за аналогичный период прошлого года. Аналогичная обработка |
| rows[\*].cells[4].value (если есть) | value\_prior2\_year | Значение за второй предшествующий период (опционально) |
| rows[\].cells[\].confidence | confidence | Агрегация уверенности: пример: 0.91 |
| Контекст группировки (заголовки разделов) | section | Определение раздела: I. ВНЕОБОРОТНЫЕ АКТИВЫ, Доходы и расходы — по ближайшему заголовку выше строки |
| Анализ стиля/текста ячейки | is\_total\_row | TRUE, если row\_name содержит Итого, БАЛАНС |
| rows[\*].explanation\_code (если есть в расширенной схеме) | explanation\_code | Код пояснений (опционально, если присутствует в структуре) |
| Системная генерация | row\_id | Автоинкремент / UUID |
| report\_id из заголовка | report\_id | FK: подставляется report\_id из только что созданной записи fin\_report\_header |
На визуальную форму выводится шапка документа со структурой:
| Параметр | Поле | Пример |
|---|---|---|
| Тип отчета | report\_type | Баланс |
| Наименование организации | Organizations.full\_name по ключу org\_id | Акционерное общество Русский бисквит |
| ИНН | Organizations.inn по ключу org\_id | 3528052718 |
| Единица измерения | unit | тыс. руб. |
| Отчетная дата | reporting\_date | 30.09.2025 |
и табличной частью на основании данных таблицы F in\_report\_rows :
| Пояснение | Наименование показателя | Код | Период 1 (rows[0].cells[2].value) | Период 2 (rows[0].cells[3].value) | Период 3 (rows[0].cells[4].value) |
|---|---|---|---|---|---|
| row\_name | row\_code | value\_current | value\_prior\_year | value\_prior2\_year |
После того как парсинг по всем документам завершен (созданы и заполнены все таблицы по всем организациям участвующих в своде, создана сводная ОСВ, запись переходит в статус CREATED (черновик)