При оптимизации быстродействия отчетов и алгоритмов проведения в 1С:Предприятии 7.7 разработчики часто прибегают к прямому чтению базы данных с помощью внешней компоненты 1С++. Однако при попытке составить прямой запрос к бухгалтерской подсистеме многие сталкиваются с трудностями: в платформе 1С 7.7 нет понятия «Регистр бухгалтерии», появившегося только в восьмой версии, а структура хранения бухгалтерских данных значительно отличается от оперативного учета. Разберем детально, как устроена бухгалтерская база в 7.7 и как правильно строить прямые запросы к проводкам и сальдо.
Особенности архитектуры бухгалтерского контура 1С 7.7
Для корректного составления SQL-запросов проанализируем, где и как 1С 7.7 сохраняет бухгалтерские операции:
Разделение операций и проводок: заголовочные данные хранятся отдельно от самих проводок. Таблица операций связывает проводку с документом-регистратором или ручной операцией, а таблица проводок фиксирует сам факт движения (дебет, кредит, сумма, количество, валюта).
Таблицы итогов: чтобы не пересчитывать баланс с момента создания базы, платформа рассчитывает и сохраняет агрегированные данные помесячно в отдельных таблицах итогов (отдельно по счетам и отдельно по счетам с субконто).
Полиморфный тип субконто: аналитика в таблицах итогов и проводок имеет составной тип данных. Значение субконто хранится в виде строки, содержащей префикс типа метаданных и идентификатор самого объекта, поэтому для соединения со справочниками требуется либо отсечение префикса, либо использование встроенного макропарсера метаданных 1С++.
Забалансовые счета: учет на забалансовых счетах ведется без принципа двойной записи, поэтому в проводке может присутствовать только счет дебета либо только счет кредита.
Подходы к чтению данных: классы и прямые SQL-запросы
Рассмотрим два основных пути выборки данных бухгалтерского учета через 1С++:
Использование специализированных классов: в сообществе разработчиков 1С++ классическим стандартом для работы с проводками и итогами является класс AccountRecordSet (авторство И. Берездецкого). Данный класс инкапсулирует в себе сложные алгоритмы парсинга полиморфных полей и механизм расчета остатков на контрольные даты, освобождая разработчика от ручного написания многостраничных SQL-запросов.
Формирование прямых SQL-запросов вручную: прямое обращение через объект ODBCRecordSet к таблицам проводок и итогов. Этот подход дает максимальный контроль над производительностью, но требует глубокого понимания логики расчета сальдо.
Алгоритм расчета оборотов и остатков через прямой запрос
Разберем по шагам, как вычислять числовые показатели бухгалтерского учета через прямой SQL:
Расчет оборотов за период: наиболее простая операция. Для получения дебетовых и кредитовых оборотов достаточно выполнить суммирование полей суммы (и количества) непосредственно из таблицы проводок, наложив фильтр по интервалу дат и счетам дебета/кредита.
Расчет остатков на начало/конец месяца: для этой задачи не следует сканировать всю таблицу проводок. Выборка осуществляется непосредственно из таблицы итогов за соответствующий закрытый расчетный месяц.
Расчет остатков на произвольную дату и время: чтение проводок «с начала времен» на реальной базе приведет к недопустимым задержкам. Корректный алгоритм состоит в получении базового остатка на начало ближайшего месяца из таблицы итогов с последующим доначислением оборотов по таблице проводок за период от границы месяца до целевой точки времени.
Важные ограничения при работе напрямую с базой
Выясним причины возможных расхождений прямых запросов со штатными отчетами 1С:
Развернутое сальдо на активно-пассивных счетах: если просто сложить дебет и кредит по всему счету, получится «свернутое» сальдо, что грубо искажает учетную картину. Корректное сальдо для активно-пассивных счетов требует обязательной группировки по всем уровням субконто счета и вычисления знака остатка для каждого элемента аналитики отдельно.
Точка актуальности итогов: таблицы итогов содержат корректные цифры только в том случае, если расчет бухгалтерских итогов в базе актуален. Если границы итогов не сдвинуты регламентно, запрос к итогам вернет устаревшие данные.
Сложность учета по позиции: если требуется остаток строго на момент проведения документа внутри дня, выборку проводок необходимо фильтровать не просто по дате, а по составному полю позиции документа (дата, время и идентификатор документа в журнале).
Посмотрим на пример базовой структуры обращения к базе через 1С++ при использовании макроподстановок метаданных:
Запрос = СоздатьОбъект("ODBCRecordSet");
ТекстЗапроса = "
|SELECT
| ЖРН.DATE_TIME_IDDOC,
| ПРВ.Debet,
| ПРВ.Kredit,
| ПРВ.Sum
|FROM
| $Операция AS ОПЕР
|INNER JOIN
| $Проводки AS ПРВ ON ПРВ.DOCID = ОПЕР.DOCID
|INNER JOIN
| _1SJOURN AS ЖРН ON ЖРН.IDDOC = ОПЕР.DOCID
|WHERE
| ЖРН.DATE_TIME_IDDOC >= :НачДата AND ЖРН.DATE_TIME_IDDOC <= :КонДата
|";
Запрос.УстановитьТекстовыйПараметр("НачДата", ПозицияНачалаПериода);
Запрос.УстановитьТекстовыйПараметр("КонДата", ПозицияКонцаПериода);
ТЗ = Запрос.ВыполнитьИнструкцию(ТекстЗапроса);
Если задача требует получения сложных разрезов с развернутыми сальдо по субконто и валютам, а опыт написания сложных SQL-агрегаций ограничен, рекомендуется использовать проверенный класс AccountRecordSet или стандартный платформенный объект БухгалтерскиеИтоги, который автоматически учитывает все нюансы активно-пассивных и забалансовых счетов.