Переход разработчиков с платформы 1С:Предприятие 8 на «семерку» (или наоборот) часто вызывает вопросы из-за кардинальных различий в архитектуре метаданных. Одним из таких камней преткновения является механизм работы с бухгалтерскими итогами. В этой статье мы подробно разберем, как устроен обход проводок в 1С 7.7, выясним назначение объекта Операция и проведем детальную аналогию с запросами к физическим таблицам регистров бухгалтерии в 1С 8.
Рассмотрим фрагмент кода, который вызвал вопросы у разработчика, привыкшего к механизмам «восьмерки»:
Ит.ВыполнитьЗапрос(ВыбНачПериода, ВыбКонПериода, ВыбСчет,,, 1, "Проводка", "С");
Ит.ВыбратьПериоды();
Пока Ит.ПолучитьПериод() = 1 Цикл
Опер = Ит.Операция;
КонецЦикла;
Разберем по шагам, что здесь происходит. Объект Ит представляет собой БухгалтерскиеИтоги. Метод ВыполнитьЗапрос инициализирует расчет итогов по заданным параметрам.
Обратим особое внимание на параметры метода:
ПолучитьПериод() не позволил бы нам спозиционироваться на детальных движениях.После выполнения запроса мы открываем выборку периодов с помощью Ит.ВыбратьПериоды(). Цикл Пока Ит.ПолучитьПериод() = 1 последовательно обходит каждую проводку, попавшую в условия фильтрации. На каждом шаге цикла свойство Ит.Операция возвращает специальный системный объект — Операция.
В платформе 1С 7.7 проводки не могут существовать обособленно. Каждая проводка жестко подчинена объекту типа Операция. В свою очередь, операция всегда неразрывно связана с конкретным документом-регистратором (или создается вручную через специализированный интерфейс ручных операций, который под капотом также генерирует документ «Операция»).
Проанализируем структуру хранения данных в SQL-версии 1С 7.7. Операции хранятся в физической таблице 1SOPER, а сами проводки — в таблице 1SENTRY. Связь между ними осуществляется по уникальным внутренним идентификаторам.
Когда мы пишем:
Опер = Ит.Операция;
мы получаем доступ к контексту операции, которая породила текущую проводку в цикле. Через этот объект мы можем обратиться к реквизитам самой операции, а также к аналитике конкретной проводки:
Опер.Дебет.Счет — счет дебета текущей проводки.Опер.Кредит.Счет — счет кредита текущей проводки.Опер.Сумма — сумма проводки.Для получения самого документа-источника в 1С 7.7 используется конструкция:
РеальныйДокумент = Опер.Документ.ТекущийДокумент();
Выясним причину, почему нельзя написать просто Опер.Документ. Дело в том, что свойство Опер.Документ возвращает обобщенный объект типа «Документ». Из-за архитектурных ограничений типизации «семерки» у этого обобщенного объекта нет доступа к специфическим реквизитам конкретного вида документа (например, к реквизитам «Склад» или «Контрагент» документа «РасходнаяНакладная»).
Метод ТекущийДокумент() выполняет приведение типов. Он определяет реальный вид документа-регистратора и возвращает ссылку на конкретный объект метаданных, позволяя программисту без ограничений считывать любые его реквизиты и табличные части.
Отвечая на вопрос автора темы: да, этот код концептуально решает ту же задачу, что и прямой запрос к физической таблице регистра бухгалтерии в 1С 8 без использования виртуальных таблиц оборотов и остатков.
В платформе 1С 8 архитекторы отказались от сущности Операция. Записи регистра бухгалтерии (движения) подчинены напрямую документу-регистратору через стандартный реквизит Регистратор. Ручные операции в «восьмерке» — это обычные документы конфигурации (например, документ «ОперацияБух»), которые делают записи в регистр напрямую.
Посмотрим на пример аналогичного запроса в 1С 8. Если бы нам потребовалось получить список проводок с их реквизитами напрямую, мы бы обратились к физической таблице движений РегистрБухгалтерии.Хозрасчетный:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Хозрасчетный.Регистратор КАК Документ,
| Хозрасчетный.СчетДт КАК СчетДт,
| Хозрасчетный.СчетКт КАК СчетКт,
| Хозрасчетный.Сумма КАК Сумма
|ИЗ
| РегистрБухгалтерии.Хозрасчетный КАК Хозрасчетный
|ГДЕ
| Хозрасчетный.Период МЕЖДУ &НачПериода И &КонПериода
| И (Хозрасчетный.СчетДт = &ВыбСчет ИЛИ Хозрасчетный.СчетКт = &ВыбСчет)";
Запрос.УстановитьПараметр("НачПериода", ВыбНачПериода);
Запрос.УстановитьПараметр("КонПериода", ВыбКонПериода);
Запрос.УстановитьПараметр("ВыбСчет", ВыбСчет);
Результат = Запрос.Выполнить();
Этот запрос в 1С 8 выполняет абсолютно то же самое действие, что и метод ВыполнитьЗапрос(..., "Проводка") в 1С 7.7. Мы получаем сырые, неагрегированные данные по проводкам за указанный период, отфильтрованные по конкретному счету.
Важно помнить о техническом нюансе: метод ВыполнитьЗапрос в 1С 7.7 при детализации до проводки на больших базах данных работает крайне неэффективно. Платформа формирует сложные SQL-запросы к таблицам итогов и проводок, пытаясь собрать данные в локальной памяти компьютера пользователя. Это часто приводило к зависаниям и низкой скорости формирования отчетов.
Именно поэтому опытные разработчики в «семерке» при необходимости обойти миллионы проводок часто отказывались от штатных БухгалтерскихИтогов и писали «прямые запросы» к SQL-таблицам 1SENTRY и 1SOPER с помощью внешних компонент (таких как 1С++ или через интерфейс ADO). В 1С 8 благодаря декларативному языку запросов и СУБД-ориентированной архитектуре эта проблема больше не стоит так остро, поскольку СУБД самостоятельно строит оптимальный план выполнения запроса к регистру.