При работе с запросами к регистрам в платформе 1С 7.7 разработчики часто сталкиваются с ситуацией, когда итоговая таблица выводит неполный остаток. Например, по данным регистра товар пришел тремя партиями общим количеством 8 штук, но при обходе группировок запрос возвращает «1» — количество из первой поступившей партии.
Проанализируем причины возникновения этой проблемы, разберем ошибку в алгоритме и рассмотрим правильные способы получения итоговых остатков и суммовых показателей.
Рассмотрим пример кода запроса и его обхода, в котором проявляется проблема:
Запрос = СоздатьОбъект("Запрос");
ТекстЗапроса = "
|Период с ВыбНачПериода по ДатаОст;
|Обрабатывать НеПомеченныеНаУдаление;
|Фирма = Регистр.СкладУчетПарт.Фирма;
|Товар = Регистр.СкладУчетПарт.Номенклатура;
|Колво = Регистр.СкладУчетПарт.Количество;
|Цена = Регистр.СкладУчетПарт.УчетнаяЦена;
|Функция ТовОстаток = КонОст(Колво);
|Группировка Фирма без групп;
|Группировка Товар без групп;
|";
Запрос.Выполнить(ТекстЗапроса);
Пока Запрос.Группировка(1) = 1 Цикл
Пока Запрос.Группировка(2) = 1 Цикл
СводнаяТаблица.НоваяСтрока();
СводнаяТаблица.Номенклатура = Запрос.Товар;
СводнаяТаблица.Количество = Запрос.Колво; // Ошибка здесь!
КонецЦикла;
КонецЦикла;
Выясним, почему Запрос.Колво выводит значение 1 вместо 8. В языке запросов 1С 7.7 прямое обращение к переменной регистра (в данном случае Запрос.Колво или Запрос.Цена), для которой не определена агрегатная функция и по которой не задана группировка, возвращает значение реквизита только из первой найденной записи (движения) внутри текущей группы.
Для того чтобы получить свернутый конечный остаток номенклатуры по всей группировке, необходимо обращаться исключительно к значению функции запроса — Запрос.ТовОстаток.
Чтобы получить верное общее количество номенклатуры с учетом всех партий, заменим прямое обращение к переменной регистра на функцию запроса.
Разберем исправленный фрагмент обхода группировок:
Пока Запрос.Группировка(1) = 1 Цикл
Пока Запрос.Группировка(2) = 1 Цикл
СводнаяТаблица.НоваяСтрока();
СводнаяТаблица.Номенклатура = Запрос.Товар;
// Обращаемся к агрегатной функции запроса:
СводнаяТаблица.Количество = Запрос.ТовОстаток;
КонецЦикла;
КонецЦикла;
Рассмотрим еще один важный технический момент. Если номенклатура поступала партиями с различной учетной ценой, то умножение Запрос.Цена * Запрос.ТовОстаток приведет к ошибке в учете. Переменная Запрос.Цена вернет стоимость первой попавшейся партии, что исказит итоговую сумму остатка.
Для корректного расчета сумм и средних цен применим следующий подход:
Функция СуммаОст = КонОст(Сумма);.Запрос.СуммаОст.Запрос.СуммаОст / Запрос.ТовОстаток.Посмотрим на пример правильного текста запроса:
ТекстЗапроса = "
|Период по ДатаОст;
|Обрабатывать НеПомеченныеНаУдаление;
|Фирма = Регистр.СкладУчетПарт.Фирма;
|Товар = Регистр.СкладУчетПарт.Номенклатура;
|Колво = Регистр.СкладУчетПарт.Количество;
|Сумма = Регистр.СкладУчетПарт.Сумма;
|Функция ТовОстаток = КонОст(Колво);
|Функция СуммаОстаток = КонОст(Сумма);
|Группировка Фирма без групп;
|Группировка Товар без групп;
|Условие(Фирма в СписокФирм);
|";
Теперь проанализируем заполнение таблицы значений с расчетом показателей:
Пока Запрос.Группировка("Фирма") = 1 Цикл
Пока Запрос.Группировка("Товар") = 1 Цикл
ОстКолво = Запрос.ТовОстаток;
ОстСумма = Запрос.СуммаОстаток;
СредняяЦена = ?(ОстКолво <> 0, ОстСумма / ОстКолво, 0);
СводнаяТаблица.НоваяСтрока();
СводнаяТаблица.Номенклатура = Запрос.Товар;
СводнаяТаблица.Количество = ОстКолво;
СводнаяТаблица.Цена = СредняяЦена;
СводнаяТаблица.Сумма = ОстСумма;
КонецЦикла;
КонецЦикла;
Рассмотрим правила, которые помогут избежать ошибок и повысят скорость работы обработок в 1С 7.7:
НоваяКолонка() внутри циклов выборки изменяют структуру таблицы на лету, что приводит к сдвигу индексов и некорректной записи значений. Структура колонок должна создаваться заранее.Если ФирмаОбщ = Фирма1...) используйте конструкцию Условие() в тексте запроса. Это снизит объем данных, передаваемых из БД.Период по ДатаОст; без начальной даты Период с.... Это избавит платформу от лишнего расчета оборотов за период.