Приветствуем, коллеги! Давайте вместе разберем одну из распространенных ситуаций при работе с регистрами накопления в 1С, когда в поле количества упорно записывается "1", хотя мы ожидаем совершенно другое значение. Эта проблема, как правило, связана с некорректным использованием метода Количество() объекта ВыборкаИзРезультатаЗапроса.
ВыборкаИзРезультатаЗапроса.Количество()?Прежде всего, давайте разберемся с поведением метода Количество() при работе с выборками результата запроса. Метод ВыборкаИзРезультатаЗапроса.Количество() возвращает не общее число строк во всем результате запроса, а число записей в текущем уровне выборки. Это очень важное отличие.
Рассмотрим подробнее: если ваш запрос содержит группировки, то выборка может быть иерархической. В таком случае, при обходе выборки, Количество() будет возвращать число элементов только для текущего уровня группировки. Например, если вы выбрали "Товары" и сгруппировали их по "Группам", то на уровне "Группа" метод Количество() покажет, сколько групп находится на этом уровне, а не сколько всего товаров в выборке.
Выборка.Количество() может возвращать 1?Существует несколько распространенных сценариев, когда метод Количество() неожиданно для нас возвращает значение "1". Давайте их рассмотрим:
СУММА(), МАКСИМУМ(), КОЛИЧЕСТВО(*)) без дополнительных группировок. В таком случае, выборка действительно будет состоять из одной записи, и Выборка.Количество() вернет 1.Выборка.Следующий(), на каждой итерации переменная Выборка (или как вы ее назвали, например, ВыборкаТовары) представляет собой одну текущую запись. Соответственно, в этом контексте, при каждом обращении к ВыборкаТовары.Количество(), вы всегда будете получать "1", поскольку текущий элемент выборки всегда один.РезультатЗапроса.Выбрать().Количество() сразу после выполнения запроса даст общее число строк в плоской выборке. Хотя в некоторых простых случаях это может сработать, метод с явным использованием агрегатной функции КОЛИЧЕСТВО(*) в тексте запроса считается более надежным и предсказуемым, особенно при сложных запросах.Именно эти причины приводят к тому, что в вашем регистре накопления "Остатки по расходу" в поле, которое, предположительно, должно содержать фактическое списываемое количество, записывается "1". Фраза ОсталосьСписать = ВыборкаТовары.Количество() из исходного сообщения на форуме ярко демонстрирует эту проблему: если ВыборкаТовары находится в цикле обхода или является результатом запроса, вернувшего одну итоговую строку, то ОсталосьСписать всегда будет равно 1.
Давайте рассмотрим, как правильно получить общее количество записей, если именно это нам требуется, или как получить нужное количество для записи в регистр.
КОЛИЧЕСТВО(*) в тексте запросаЭто наиболее надежный и рекомендуемый способ получить общее число записей, соответствующих условиям запроса. Мы включаем в текст запроса агрегатную функцию КОЛИЧЕСТВО(*) (или КОЛИЧЕСТВО(Различные <Поле>), если нужно считать уникальные значения) как отдельное поле. Затем мы получаем значение этого поля из результата запроса.
Посмотрим на пример:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| КОЛИЧЕСТВО(*) КАК ОбщееКоличествоСтрок
| ИЗ
| Документ.ПриходнаяНакладная.Товары КАК Товары
| ГДЕ
| Товары.Ссылка = &СсылкаДокумента";
Запрос.УстановитьПараметр("СсылкаДокумента", СсылкаНашДокумент);
Результат = Запрос.Выполнить();
Выборка = Результат.Выбрать();
Если Выборка.Следующий() Тогда
ОбщееКоличествоЗаписей = Выборка.ОбщееКоличествоСтрок;
// Теперь переменная ОбщееКоличествоЗаписей содержит фактическое количество строк из таблицы Товары
// Именно это значение мы можем использовать для записи в регистр накопления,
// если оно является необходимым для учета.
КонецЕсли;
В этом случае, переменная ОбщееКоличествоЗаписей будет содержать точное количество записей, которые удовлетворяют условиям вашего запроса, и это значение не будет равно 1, если строк было больше. Это значение вы можете использовать для движений регистра накопления.
Количество() сразу после РезультатЗапроса.Выбрать()Если ваш запрос не содержит группировок, которые создают иерархическую выборку, то теоретически РезультатЗапроса.Выбрать().Количество() должен вернуть общее количество записей. Однако, как мы уже отметили, метод с КОЛИЧЕСТВО(*) считается более универсальным, явным и предсказуемым.
Пример:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Товары.Номенклатура,
| Товары.Количество
| ИЗ
| Документ.ПриходнаяНакладная.Товары КАК Товары
| ГДЕ
| Товары.Ссылка = &СсылкаДокумента";
Запрос.УстановитьПараметр("СсылкаДокумента", СсылкаНашДокумент);
Результат = Запрос.Выполнить();
Выборка = Результат.Выбрать();
ОбщееКоличествоСтрокВВыборке = Выборка.Количество();
// В этом случае Выборка.Количество() вернет число строк,
// если выборка не иерархическая и метод вызван один раз до начала обхода.
Важно: Даже в этом случае, если вам нужно получить количество, например, для каждого конкретного товара из выборки, а не общее число строк, то следует обращаться к полю Товары.Количество из самого запроса, а не к методу Выборка.Количество().
Регистры накопления типа "Остатки" предназначены для учета текущих количественных или суммовых остатков по определенным измерениям. Они фиксируют движения (приходы и расходы), которые изменяют эти остатки. Если в поле "Количество" такого регистра всегда записывается "1", это практически всегда является ошибкой логики учета.
Выясним причину: регистр накопления должен отражать фактическое списываемое или приходуемое количество товара, материала, денежных средств и так далее. Если в поле Количество всегда записывается 1, это означает, что каждый раз списывается одна единица, независимо от реального объема операции. Это не соответствует большинству бизнес-процессов.
Исходя из сообщения на форуме ОсталосьСписать = ВыборкаТовары.Количество(), источником проблемы является неверный расчет поля ОсталосьСписать. Вместо фактического количества, которое нужно списать (например, из табличной части документа), туда попадает результат метода, который считает строки выборки, а не количество единиц товара.
На форуме прозвучала рекомендация "(8) Скобки убери". Давайте разберем этот момент.
В 1С, метод Количество(), используемый для подсчета элементов выборки, всегда вызывается с пустыми скобками: Выборка.Количество(). Это стандартный синтаксис для вызова метода объекта.
Убрать скобки имеет смысл только в одном случае: если Количество является именем поля в самой выборке. То есть, если ваш запрос возвращает поле с именем "Количество", и вы хотите получить его значение. Например, если в запросе было ВЫБРАТЬ Товары.Количество КАК МоеКоличество ..., то для получения значения этого поля вы бы использовали ВыборкаТовары.МоеКоличество (без скобок). В этом случае, МоеКоличество – это уже не метод, а свойство (поле) текущей строки выборки, содержащее непосредственно значение количества из запроса.
Таким образом, для получения количества строк выборки необходимо использовать именно метод со скобками: Выборка.Количество(). Если же вам нужно получить значение поля "Количество" из текущей строки выборки, тогда вы обращаетесь к нему как к свойству: Выборка.Количество (без скобок, предполагая, что поле в запросе было названо "Количество").
Вывод: для корректного заполнения регистров накопления всегда используйте фактическое количество из полей запроса или табличных частей документов, а не счетчик строк выборки, который возвращает метод Количество(). Если вам нужно получить общее количество строк, используйте агрегатную функцию КОЛИЧЕСТВО(*) в запросе.