Почему не заполняется цена номенклатуры в табличной части 1С и как это исправить

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

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

Основные ошибки в коде заполнения цен

Проанализируем исходный фрагмент кода, в котором возникла проблема:


&НаКлиенте
Процедура ЗаполнитьЦены(Команда)
    Для Каждого строка Из Объект.Запасы Цикл
        Цена = ПолучитьЦеныНом(строка.Номенклатура, Объект.Контрагент);        
        данные = Элементы.Запасы.ТекущиеДанные;
        данные.Цена = Цена;
    КонецЦикла;
КонецПроцедуры

&НаСервере
Функция ПолучитьЦеныНом(Номенклатура, Контрагент)
    отбор = Новый Структура;
    отбор.Вставить("Номенклатура", Номенклатура);
    отбор.Вставить("ВидЦен", Справочники.ДоговорыКонтрагентов.Выбрать(, Контрагент).ВидЦен);
    Возврат РегистрыСведений.ЦеныНоменклатуры.ПолучитьПоследнее(, отбор).Цена;    
КонецФункции

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

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

Исправление ошибок и пошаговая реализация

Разберем по шагам, как правильно переписать данный механизм.

Шаг 1. Перенос обработки табличной части на сервер

Для повышения производительности весь цикл перебора строк и получения цен необходимо выполнять в едином серверном вызове. На клиенте остается только команда вызова процедур.

Шаг 2. Корректное получение вида цен и среза данных

Вместо обхода выборки договора правильнее обращаться к договору, указанному непосредственно в шапке документа (например, Объект.ДоговорКонтрагента), либо считывать вид цен через прямой реквизит договора. При получении цены из регистра сведений следует обязательно передавать дату документа Объект.Дата.

Рассмотрим правильный вариант реализации с переносом логики на сервер:


&НаКлиенте
Процедура ЗаполнитьЦены(Команда)
    ЗаполнитьЦеныНаСервере();
КонецПроцедуры

&НаСервере
Процедура ЗаполнитьЦеныНаСервере()
    
    // Проверяем заполнение договора в документе
    Если НЕ ЗначениеЗаполнено(Объект.ДоговорКонтрагента) Тогда
        Возврат;
    КонецЕсли;
    
    ВидЦен = Объект.ДоговорКонтрагента.ВидЦен;
    
    Отбор = Новый Структура;
    Отбор.Вставить("ВидЦен", ВидЦен);
    
    Для Каждого СтрокаТЧ Из Объект.Запасы Цикл
        Отбор.Вставить("Номенклатура", СтрокаТЧ.Номенклатура);
        
        // Получаем структуру с последними значениями реквизитов регистра на дату документа
        СтруктураЦены = РегистрыСведений.ЦеныНоменклатуры.ПолучитьПоследнее(Объект.Дата, Отбор);
        
        СтрокаТЧ.Цена = СтруктураЦены.Цена;
    КонецЦикла;
    
КонецПроцедуры

Оптимальный способ: применение запроса или среза последних

Поштучный вызов метода ПолучитьПоследнее() внутри цикла даже на сервере создает лишние обращения к базе данных. Наиболее оптимальным решением является использование запроса к виртуальной таблице РегистрСведений.ЦеныНоменклатуры.СрезПоследних.

Посмотрим на пример заполнения цен единым запросом:


&НаСервере
Процедура ЗаполнитьЦеныЗапросомНаСервере()
    
    ВидЦен = Объект.ДоговорКонтрагента.ВидЦен;
    
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ
        |   Запасы.Номенклатура КАК Номенклатура
        |ПОМЕСТИТЬ ВТ_Номенклатура
        |ИЗ
        |   &ТЧЗапасы КАК Запасы
        |;
        |
        |ВЫБРАТЬ
        |   ВТ.Номенклатура КАК Номенклатура,
        |   ЕСТЬNULL(Цены.Цена, 0) КАК Цена
        |ИЗ
        |   ВТ_Номенклатура КАК ВТ
        |   ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ЦеныНоменклатуры.СрезПоследних(&Период, ВидЦен = &ВидЦен) КАК Цены
        |   ПО ВТ.Номенклатура = Цены.Номенклатура";
        
    Запрос.УстановитьПараметр("Период", Объект.Дата);
    Запрос.УстановитьПараметр("ВидЦен", ВидЦен);
    Запрос.УстановитьПараметр("ТЧЗапасы", Объект.Запасы.Выгрузить());
    
    Результат = Запрос.Выполнить().Выгрузить();
    
    Для Каждого СтрокаТЧ Из Объект.Запасы Цикл
        НайденнаяСтрока = Результат.Найти(СтрокаТЧ.Номенклатура, "Номенклатура");
        Если НайденнаяСтрока <> Неопределено Тогда
            СтрокаТЧ.Цена = НайденнаяСтрока.Цена;
        КонецЕсли;
    КонецЦикла;
    
КонецПроцедуры

Что делать, если не работает точка останова на сервере

Если при попытке отладки точка останова внутри процедуры с директивой &НаСервере проскакивает и не срабатывает, необходимо проверить настройки сервера 1С:Предприятие:

  1. Выясним, включен ли режим отладки на сервере приложений. Для этого в службе 1C:Enterprise 8.3 Server Daemon должен быть установлен ключ запуска -debug.
  2. В конфигураторе открываем меню Отладка – Подключение и проверяем, что серверный сеанс присутствует в списке предметов отладки и подключен.

Соблюдение правил работы с выборками справочников, корректный доступ к строкам табличной части и запуск тяжелых операций на сервере позволяют гарантировать правильное и быстрое заполнение цен в документах 1С.

← На главную