При разработке отчетов и обработок на платформе 1С программисты часто сталкиваются с задачей выгрузки актуального остатка или дневного оборота денежных средств. Распространенной ошибкой является попытка рассчитать конечный остаток путем сплошного обхода и вычитания сумм из виртуальной таблицы Обороты с детализацией по каждому документ-регистратору.
В этой статье мы подробно разберем, чем отличаются таблицы Остатки и Обороты, как правильно передавать параметры виртуальных таблиц и оптимизировать запрос для получения точных данных.
Перед написанием запроса необходимо четко определить бизнес-требование:
Обороты.Остатки.Рассмотрим реализацию обеих задач по шагам.
Для получения итогового остатка необходимо задействовать виртуальную таблицу Остатки регистра накопления. Запрос по оборотному регистру для этих целей не подходит, так как платформе придется суммировать все движения с самого начала ведения учета, что создает огромную нагрузку на СУБД.
Проанализируем пример корректного запроса для получения остатка на конец текущего дня:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ЕСТЬNULL(ДвижениеДенегОстатки.СуммаОстаток, 0) КАК СуммаОстаток
|ИЗ
| РегистрНакопления.ДвижениеДенег.Остатки(&Период, ) КАК ДвижениеДенегОстатки";
// Для точного включения всех документов текущего дня используем объект Граница
КонецТекущегоДня = КонецДня(ТекущаяДата());
ГраницаПериода = Новый Граница(КонецТекущегоДня, ВидГраницы.Включая);
Запрос.УстановитьПараметр("Период", ГраницаПериода);
РезультатЗапроса = Запрос.Выполнить();
Выборка = РезультатЗапроса.Выбрать();
Если Выборка.Следующий() Тогда
ЭлементыФормы.КассоваяКнига.Заголовок = Выборка.СуммаОстаток;
КонецЕсли;
Обратим внимание на технические детали данного кода:
Граница: передача простой даты вида КонецДня(ТекущаяДата()) может исключить из расчета документы, проведенные ровно в последнюю секунду дня (23:59:59). Использование объекта Граница с типом ВидГраницы.Включая гарантирует учет всех операций текущего дня.NULL: функция ЕСТЬNULL защищает от получения пустого значения, если в регистре нет остатков.Пока Выборка.Следующий() не требуется.Если задача состоит в том, чтобы выдать разницу между приходом и расходом за текущие сутки, используем виртуальную таблицу Обороты. Параметры дат следует передавать строго в скобках самой таблицы.
Посмотрим на пример запроса для расчета оборота за день:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ЕСТЬNULL(ДвижениеДенегОбороты.СуммаДоходаОборот, 0) КАК СуммаДоходаОборот,
| ЕСТЬNULL(ДвижениеДенегОбороты.СуммаРасходаОборот, 0) КАК СуммаРасходаОборот,
| ЕСТЬNULL(ДвижениеДенегОбороты.СуммаДоходаОборот, 0) - ЕСТЬNULL(ДвижениеДенегОбороты.СуммаРасходаОборот, 0) КАК ДневнойРезультат
|ИЗ
| РегистрНакопления.ДвижениеДенег.Обороты(&НачалоПериода, &КонецПериода, , ) КАК ДвижениеДенегОбороты";
Запрос.УстановитьПараметр("НачалоПериода", НачалоДня(ТекущаяДата()));
Запрос.УстановитьПараметр("КонецПериода", КонецДня(ТекущаяДата()));
РезультатЗапроса = Запрос.Выполнить();
Выборка = РезультатЗапроса.Выбрать();
Если Выборка.Следующий() Тогда
ЭлементыФормы.КассоваяКнига.Заголовок = Выборка.ДневнойРезультат;
КонецЕсли;
Выясним основные причины ошибок при составлении запросов к регистрам накопления:
Регистратор: если третьим параметром виртуальной таблицы Обороты передать значение Регистратор, платформа детализирует итоги по каждому документу. Вместо одной итоговой строки сформируется массив строк, а результат в цикле будет постоянно перезаписываться значением последнего документа.ГДЕ: при использовании отбора по периоду в секции ГДЕ платформа сначала прочитает всю таблицу движений за всё время, и лишь затем отфильтрует её. Передача дат в параметры виртуальной таблицы Обороты(&НачалоПериода, &КонецПериода) заставляет СУБД рассчитывать итоги только за указанный интервал.Остатки доступна только для регистров накопления с видом «Остатки». Если регистр имеет вид «Обороты», получить накопленный остаток штатной виртуальной таблицей невозможно.