--- code: DOC-037740 title: "FNR01 Модуль загрузки документов и создания записи" --- # FNR01 Модуль загрузки документов и создания записи ## ОСВ ### Алгоритм работы: 1. Пользователь авторизуется в системе 2. Система отрисовывает списочную форму сформированных консолидированных отчетов. Пользователь нажимает "Сформировать новый отчет" 3. Система отрисовывает модальное окно для выбора ГСК -> указания отчетного периода -> выбора компании -> добавления документов по компании 4. Пользователь нажимает "Добавить" и выбирает документы для загрузки (допустимые форматы файлов xls, xlsx). 5. Для каждого загружаемого файла ОСВ система создает запись в таблице **stored\\_file** 6. После создания записи **stored\\_file** система создает задание на распознавание в таблице **recognition\\_job.** 7. Сервис выполняет парсинг файла ОСВ. Для каждой распознанной расчетной строки система создает отдельную запись в таблице **osv\\_entry**. 8. После получения **account\\_code** система выполняет нормализацию счета и поиск соответствующей записи в справочнике **chart\\_of\\_account**. 1. в **osv\\_entry.account\\_id** записывается **chart\\_of\\_account.id.** 2. в **osv\\_entry.account\\_root** записывается корневой счет. 9. При наличии в строке контрагента система выполняет его сопоставление со справочником **legal\\_entity**. Если найдено единственное соответствие, в **osv\\_entry.counterparty\\_id** записывается **legal\\_entity.id**. Выполняется последовательный поиск по: 1. **counterparty\\_inn** 2. **legal\\_entity.name** 3. **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 (черновик)