Как получить начальное сальдо, обороты по корсчетам и конечное сальдо через прямой запрос 1С++

Программист 1С v7.7 Бухгалтерский учет IT и автоматизация бизнеса
← На главную

При работе с высоконагруженными базами данных на платформе 1С 7.7 стандартные механизмы бухгалтерских итогов могут работать недостаточно быстро. В таких случаях разработчики прибегают к использованию внешних компонент, таких как 1С++, для написания прямых SQL-запросов. Одной из наиболее сложных задач является получение одновременно остатков и оборотов в разрезе корреспондирующих счетов. Разберем подробно, как это реализовать.

Понимание структуры хранения данных

Для начала проанализируем, где именно 1С 7.7 хранит бухгалтерскую информацию. В отличие от объектной модели 1С, в SQL-базе нет готовых виртуальных таблиц "ОстаткиИОбороты". Нам придется работать с первичными данными:

  1. _1SENTRY — основная таблица проводок. Здесь хранятся все движения: счет дебета (ACCDTID), счет кредита (ACCKTID), сумма (SUM) и валютные суммы. Именно из этой таблицы мы будем извлекать корреспондирующие счета.
  2. _1SBKTTL — таблица итогов по счетам. В ней хранятся рассчитанные остатки на начало периодов (обычно на начало месяца).
  3. _1SBKTTLC — таблица итогов по субконто. Она необходима, если нам нужно получить развернутое сальдо в разрезе аналитики.
  4. _1SACCNT — план счетов. Здесь хранятся идентификаторы счетов и их человекочитаемые коды (например, "60.1").

Алгоритм получения данных "все в одном"

Поскольку "Корсчет" существует только в момент совершения проводки, а "Сальдо" — это накопленный итог, нам нужно объединить данные из разных источников. Рассмотрим пошаговый алгоритм, который обычно применяется в прямых запросах:

  1. Получение входящего остатка: Мы обращаемся к таблице _1SBKTTL на ближайшую точку рассчитанных итогов (дата, предшествующая началу периода).
  2. "Добор" оборотов до даты начала: Если наш отчет начинается не с первого числа месяца, нам нужно прибавить к итогам обороты из _1SENTRY за период от начала месяца до даты начала отчета.
  3. Получение оборотов с корсчетами: Выбираем данные из _1SENTRY непосредственно за период отчета, группируя их по счету и корсчету.
  4. Расчет конечного сальдо: Математически суммируем входящий остаток и полученные обороты.

Реализация получения оборотов с корсчетами

Выясним, как правильно вытащить корреспондирующий счет. В таблице _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

Соединение остатков и оборотов через IndexedTable

Часто бывает сложно написать один монструозный SQL-запрос, который учитывал бы и итоги, и проводки с корсчетами. Проанализируем более гибкий подход, предлагаемый компонентой 1С++ — использование объекта IndexedTable (индексированная таблица).

Рассмотрим этот процесс подробнее:

  1. Создаем объект Запрос через 1С++.
  2. Первым запросом получаем начальные остатки в таблицу значений (ТЗ).
  3. Вторым запросом получаем обороты с корсчетами во вторую ТЗ.
  4. Загружаем обе таблицы в IndexedTable.
  5. Выполняем локальный SQL-запрос над этими таблицами (метод Выполнить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 и группировать результат по всем субконто, а уже на уровне формирования отчета вычислять дебетовый или кредитовый остаток для каждой комбинации аналитики.

Используя данные подходы, мы можем значительно ускорить формирование бухгалтерских отчетов любой сложности, сохраняя при этом гибкость настройки корреспондирующих счетов, которая недоступна в упрощенных таблицах итогов.

← На главную