Почему перестает работать запрос по остаткам после вызова ИспользоватьВладельца в 1С 7.7?

Программист 1С v7.7 1С:Управление торговлей Управленческий учет Торговля и дистрибуция
← На главную

При разработке обработок выгрузки номенклатуры и штрихкодов в кассовое программное обеспечение (например, Frontol) разработчики часто сталкиваются с ситуацией, когда переменная с остатком товара возвращает нулевые значения. При этом подозрение ошибочно падает на вызовы методов подчиненных справочников, таких как ИспользоватьВладельца(). Разберем детально, почему это происходит, как устроена работа с объектом Запрос в платформе 1С:Предприятие 7.7 и какими методами эффективнее получать остатки по регистру.

Анализ проблемы: влияют ли методы справочника на объект Запрос?

Проанализируем механику работы платформы 1С 7.7. Объект Справочник и объект Запрос представляют собой независимые программные сущности. Вызов метода СпрАссортименты.ИспользоватьВладельца(ТекНоменклатура) лишь устанавливает фильтр по родителю-владельцу для конкретного экземпляра объекта справочника. Он никоим образом не сбрасывает контекст ранее выполненного запроса и не влияет на обращение к регистрам.

Если после добавления логики по подчиненному справочнику остатки «пропадают» или возвращают 0, реальные причины кроются в следующем:

  1. Некорректный синтаксис группировок запроса: попытка группировать данные по числовым ресурсам регистра (например, |Группировка Колво;).
  2. Обход иерархии папок: при стандартной группировке |Группировка Товар; запрос выполняет обход вышестоящих групп справочника, у которых остаток равен нулю.
  3. Фактическое отсутствие остатка: товар на выбранном складе действительно имеет нулевой остаток на момент расчета.
  4. Избыточность использования объекта Запрос для единичной позиции: построчный вызов тяжелого запроса в цикле не только снижает производительность, но и усложняет отладку.

Исправление ошибок в тексте Запроса 1С 7.7

Посмотрим на классическую ошибку в описании секции группировок. В запросе группировка должна строиться строго по измерениям регистра или реквизитам, но не по агрегируемым ресурсам.


// Неправильно:
|Функция Количество = КонОст(Колво);
|Группировка Товар;
|Группировка Колво; // Ошибка: попытка группировки по числу

// Правильно:
|Функция Количество = КонОст(Колво);
|Группировка Товар Без Групп;
|Условие (Товар = ТекНоменклатура);
|Условие (Склад = ВыбСклад);

Обратите внимание на модификатор Без Групп. Если его не указать, при обходе выборки через Запрос.Группировка(1) первыми в цикл могут попадать родительские папки выбранной номенклатуры, для которых функция КонОст() возвращает 0. Проверим правильный вариант обхода результата:


ТекстЗапроса = 
"
|Товар = Регистр.ОстаткиТМЦ.Номенклатура;
|Склад = Регистр.ОстаткиТМЦ.Склад;
|Колво = Регистр.ОстаткиТМЦ.Количество;
|Функция Количество = КонОст(Колво);
|Группировка Товар Без Групп;
|Условие (Товар = ТекНоменклатура);
|Условие (Склад = ВыбСклад);
|";

Если Запрос.Выполнить(ТекстЗапроса) = 1 Тогда
    Пока Запрос.Группировка(1) = 1 Цикл
        Количество = Запрос.Количество;
    КонецЦикла;
КонецЕсли;

Оптимальное решение: получение остатка через СводныйОстаток()

Если в алгоритме требуется получить остаток только по одной конкретной позиции номенклатуры и одному складу, создавать объект Запрос не имеет практического смысла. Платформа 1С 7.7 предоставляет быстрый прямой метод СводныйОстаток() объекта Регистр.

Рассмотрим, как заменить громоздкий запрос всего двумя строками кода:


РегОстатки = СоздатьОбъект("Регистр.ОстаткиТМЦ");
// Порядок параметров строго соответствует структуре измерений регистра в конфигураторе:
// 1 - Номенклатура, 2 - Склад (и т.д.), последний параметр - имя ресурса
Количество = РегОстатки.СводныйОстаток(ТекНоменклатура, ВыбСклад, , "Количество");

Преимущества данного метода:

  1. Код выполняется в десятки раз быстрее, чем компиляция и выполнение текста запроса.
  2. Исключаются любые ошибки с обходом группировок и групп справочника.
  3. Значение остатка доступно сразу без циклов Пока ... Цикл.

Корректная работа с подчиненным справочником штрихкодов

Разберем правильный алгоритм обхода подчиненного справочника Ассортимент (или Штрихкоды) для выбранной номенклатуры. Метод ИспользоватьВладельца() должен вызываться перед открытием выборки элементов:


СпрАссортименты = СоздатьОбъект("Справочник.Ассортимент");

// Устанавливаем фильтр по владельцу
СпрАссортименты.ИспользоватьВладельца(ТекНоменклатура);
СпрАссортименты.ВыбратьЭлементы();

Пока СпрАссортименты.ПолучитьЭлемент() = 1 Цикл
    Если СпрАссортименты.ПометкаУдаления() = 1 Тогда
        Продолжить;
    КонецЕсли;
    
    // Формируем строку выгрузки со штрихкодом из подчиненного справочника
    СтрШтрихкод = СокрЛП(СпрАссортименты.Штрихкод);
    // Значение переменной Количество остается неизменным и доступно для записи
    текст.ДобавитьСтроку(СпрНоменклатура.Код + ";" + СтрШтрихкод + ";" + СокрЛП(Формат(Количество, "Ч10.3")));
КонецЦикла;

Рекомендации по ускорению массовой выгрузки

Если выгрузка каталога выполняется для всех товаров сразу, а не для одной выбранной позиции, используйте следующие архитектурные подходы:

  1. Пакетная выгрузка остатков: воспользуйтесь методом Регистр.ОстаткиТМЦ.ВыгрузитьИтоги(ТаблицаОстатков, ...) перед началом цикла. Это сформирует оперативную таблицу остатков в памяти, откуда значения извлекаются через метод НайтиЗначение().
  2. Кэширование подчиненных штрихкодов: сделайте один запрос по справочнику штрихкодов или выгрузите его целиком в ТаблицаЗначений, сгруппировав по колонке владельца, чтобы не выполнять построчные обращения к базе данных.
  3. Сравнение перечислений: избегайте строковых функций вида Число(Лев(СпрНоменклатура.СтавкаНДС, 2)) при определении ставок налога. Сравнивайте значение напрямую с объектами метаданных: СпрНоменклатура.СтавкаНДС = Перечисление.СтавкиНДС.НДС10.
← На главную