При разработке обработок выгрузки номенклатуры и штрихкодов в кассовое программное обеспечение (например, Frontol) разработчики часто сталкиваются с ситуацией, когда переменная с остатком товара возвращает нулевые значения. При этом подозрение ошибочно падает на вызовы методов подчиненных справочников, таких как ИспользоватьВладельца(). Разберем детально, почему это происходит, как устроена работа с объектом Запрос в платформе 1С:Предприятие 7.7 и какими методами эффективнее получать остатки по регистру.
Проанализируем механику работы платформы 1С 7.7. Объект Справочник и объект Запрос представляют собой независимые программные сущности. Вызов метода СпрАссортименты.ИспользоватьВладельца(ТекНоменклатура) лишь устанавливает фильтр по родителю-владельцу для конкретного экземпляра объекта справочника. Он никоим образом не сбрасывает контекст ранее выполненного запроса и не влияет на обращение к регистрам.
Если после добавления логики по подчиненному справочнику остатки «пропадают» или возвращают 0, реальные причины кроются в следующем:
|Группировка Колво;).|Группировка Товар; запрос выполняет обход вышестоящих групп справочника, у которых остаток равен нулю.Посмотрим на классическую ошибку в описании секции группировок. В запросе группировка должна строиться строго по измерениям регистра или реквизитам, но не по агрегируемым ресурсам.
// Неправильно:
|Функция Количество = КонОст(Колво);
|Группировка Товар;
|Группировка Колво; // Ошибка: попытка группировки по числу
// Правильно:
|Функция Количество = КонОст(Колво);
|Группировка Товар Без Групп;
|Условие (Товар = ТекНоменклатура);
|Условие (Склад = ВыбСклад);
Обратите внимание на модификатор Без Групп. Если его не указать, при обходе выборки через Запрос.Группировка(1) первыми в цикл могут попадать родительские папки выбранной номенклатуры, для которых функция КонОст() возвращает 0. Проверим правильный вариант обхода результата:
ТекстЗапроса =
"
|Товар = Регистр.ОстаткиТМЦ.Номенклатура;
|Склад = Регистр.ОстаткиТМЦ.Склад;
|Колво = Регистр.ОстаткиТМЦ.Количество;
|Функция Количество = КонОст(Колво);
|Группировка Товар Без Групп;
|Условие (Товар = ТекНоменклатура);
|Условие (Склад = ВыбСклад);
|";
Если Запрос.Выполнить(ТекстЗапроса) = 1 Тогда
Пока Запрос.Группировка(1) = 1 Цикл
Количество = Запрос.Количество;
КонецЦикла;
КонецЕсли;
Если в алгоритме требуется получить остаток только по одной конкретной позиции номенклатуры и одному складу, создавать объект Запрос не имеет практического смысла. Платформа 1С 7.7 предоставляет быстрый прямой метод СводныйОстаток() объекта Регистр.
Рассмотрим, как заменить громоздкий запрос всего двумя строками кода:
РегОстатки = СоздатьОбъект("Регистр.ОстаткиТМЦ");
// Порядок параметров строго соответствует структуре измерений регистра в конфигураторе:
// 1 - Номенклатура, 2 - Склад (и т.д.), последний параметр - имя ресурса
Количество = РегОстатки.СводныйОстаток(ТекНоменклатура, ВыбСклад, , "Количество");
Преимущества данного метода:
Пока ... Цикл.Разберем правильный алгоритм обхода подчиненного справочника Ассортимент (или Штрихкоды) для выбранной номенклатуры. Метод ИспользоватьВладельца() должен вызываться перед открытием выборки элементов:
СпрАссортименты = СоздатьОбъект("Справочник.Ассортимент");
// Устанавливаем фильтр по владельцу
СпрАссортименты.ИспользоватьВладельца(ТекНоменклатура);
СпрАссортименты.ВыбратьЭлементы();
Пока СпрАссортименты.ПолучитьЭлемент() = 1 Цикл
Если СпрАссортименты.ПометкаУдаления() = 1 Тогда
Продолжить;
КонецЕсли;
// Формируем строку выгрузки со штрихкодом из подчиненного справочника
СтрШтрихкод = СокрЛП(СпрАссортименты.Штрихкод);
// Значение переменной Количество остается неизменным и доступно для записи
текст.ДобавитьСтроку(СпрНоменклатура.Код + ";" + СтрШтрихкод + ";" + СокрЛП(Формат(Количество, "Ч10.3")));
КонецЦикла;
Если выгрузка каталога выполняется для всех товаров сразу, а не для одной выбранной позиции, используйте следующие архитектурные подходы:
Регистр.ОстаткиТМЦ.ВыгрузитьИтоги(ТаблицаОстатков, ...) перед началом цикла. Это сформирует оперативную таблицу остатков в памяти, откуда значения извлекаются через метод НайтиЗначение().ТаблицаЗначений, сгруппировав по колонке владельца, чтобы не выполнять построчные обращения к базе данных.Число(Лев(СпрНоменклатура.СтавкаНДС, 2)) при определении ставок налога. Сравнивайте значение напрямую с объектами метаданных: СпрНоменклатура.СтавкаНДС = Перечисление.СтавкиНДС.НДС10.