При работе с высоконагруженными базами данных на платформе 1С 7.7 стандартные механизмы бухгалтерских итогов могут работать недостаточно быстро. В таких случаях разработчики прибегают к использованию внешних компонент, таких как 1С++, для написания прямых SQL-запросов. Одной из наиболее сложных задач является получение одновременно остатков и оборотов в разрезе корреспондирующих счетов. Разберем подробно, как это реализовать.
Для начала проанализируем, где именно 1С 7.7 хранит бухгалтерскую информацию. В отличие от объектной модели 1С, в SQL-базе нет готовых виртуальных таблиц "ОстаткиИОбороты". Нам придется работать с первичными данными:
ACCDTID), счет кредита (ACCKTID), сумма (SUM) и валютные суммы. Именно из этой таблицы мы будем извлекать корреспондирующие счета.Поскольку "Корсчет" существует только в момент совершения проводки, а "Сальдо" — это накопленный итог, нам нужно объединить данные из разных источников. Рассмотрим пошаговый алгоритм, который обычно применяется в прямых запросах:
_1SBKTTL на ближайшую точку рассчитанных итогов (дата, предшествующая началу периода)._1SENTRY за период от начала месяца до даты начала отчета._1SENTRY непосредственно за период отчета, группируя их по счету и корсчету.Выясним, как правильно вытащить корреспондирующий счет. В таблице _1SENTRY каждая строка представляет собой проводку. Если мы анализируем обороты счета "10", то:
— Для дебетового оборота ACCDTID будет равен ID счета 10, а ACCKTID — это и будет наш корсчет.
— Для кредитового оборота ACCKTID будет равен ID счета 10, а ACCDTID станет корсчетом.
Посмотрим на пример структуры запроса для получения оборотов:
SELECT
-- Определяем основной счет и корсчет
CASE WHEN Entry.ACCDTID = :ВыбСчетID THEN Entry.ACCKTID ELSE Entry.ACCDTID END as CorAccID,
-- Суммируем дебет и кредит
SUM(CASE WHEN Entry.ACCDTID = :ВыбСчетID THEN Entry.SUM ELSE 0 END) as DebTurnover,
SUM(CASE WHEN Entry.ACCKTID = :ВыбСчетID THEN Entry.SUM ELSE 0 END) as KretTurnover
FROM
_1SENTRY as Entry
WHERE
Entry.DATE_TIME_IDDOC BETWEEN :НачДата AND :КонДата
AND (Entry.ACCDTID = :ВыбСчетID OR Entry.ACCKTID = :ВыбСчетID)
AND Entry.IS_ACTIVE = 1
GROUP BY
CASE WHEN Entry.ACCDTID = :ВыбСчетID THEN Entry.ACCKTID ELSE Entry.ACCDTID END
Часто бывает сложно написать один монструозный SQL-запрос, который учитывал бы и итоги, и проводки с корсчетами. Проанализируем более гибкий подход, предлагаемый компонентой 1С++ — использование объекта IndexedTable (индексированная таблица).
Рассмотрим этот процесс подробнее:
Запрос через 1С++.IndexedTable.ВыполнитьSQL_ИзТЗ), чтобы соединить их по идентификатору счета.Пример использования ВыполнитьSQL_ИзТЗ для объединения данных:
// Предположим, у нас есть ТЗ_Остатки и ТЗ_Обороты
ИТЗ_Остатки = СоздатьОбъект("IndexedTable");
ИТЗ_Остатки.Загрузить(ТЗ_Остатки);
ИТЗ_Обороты = СоздатьОбъект("IndexedTable");
ИТЗ_Обороты.Загрузить(ТЗ_Обороты);
ЗапросSQL = СоздатьОбъект("Запрос");
ТекстЗапроса = "
|SELECT
| COALESCE(Ost.AccID, Obor.CorAccID) as AccID,
| Ost.StartBalance,
| Obor.DebTurnover,
| Obor.KretTurnover,
| (COALESCE(Ost.StartBalance, 0) + COALESCE(Obor.DebTurnover, 0) - COALESCE(Obor.KretTurnover, 0)) as EndBalance
|FROM @ТЗ_Остатки as Ost
|FULL OUTER JOIN @ТЗ_Обороты as Obor ON Ost.AccID = Obor.CorAccID
|";
ЗапросSQL.ВыполнитьSQL_ИзТЗ(ТекстЗапроса);
Типы данных ID: Помните, что идентификаторы счетов в базе хранятся в виде 36-ричного представления (строки длиной 9 символов). При сравнении счетов в запросе всегда используйте внутренний ID, полученный через ЗначениеВСтрокеВнутр() или специальные методы метаданных 1С++.
Активность проводок: В таблице _1SENTRY поле IS_ACTIVE крайне важно. Если проводка была удалена или документ не проведен, значение будет равно 0 (или другому признаку неактивности). Обязательно фильтруйте данные по этому полю, иначе ваши итоги не сойдутся с системными.
Разделитель учета: Если в вашей конфигурации используется разделитель учета (например, по фирмам), его также необходимо включать в условия WHERE и группировки, чтобы данные не перемешались.
Развернутое сальдо: Если вы работаете с активно-пассивными счетами (например, 60 или 62), простое суммирование остатка по счету может быть некорректным. В этом случае необходимо делать запрос к таблице _1SBKTTLC и группировать результат по всем субконто, а уже на уровне формирования отчета вычислять дебетовый или кредитовый остаток для каждой комбинации аналитики.
Используя данные подходы, мы можем значительно ускорить формирование бухгалтерских отчетов любой сложности, сохраняя при этом гибкость настройки корреспондирующих счетов, которая недоступна в упрощенных таблицах итогов.