Как исправить ошибки контекста и автозаполнения кода печати в Отчете производства за смену в 1С?

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

Введение

При разработке в среде 1С:Предприятие начинающие специалисты часто сталкиваются с трудностями при обращении к реквизитам документов и табличных частей. Особенно остро эта проблема встает при работе в конфигурациях на обычных формах (таких как УПП 1.3, КА 1.1 или УТ 10.3), где логика работы с контекстом формы кардинально отличается от управляемого приложения. В этой статье мы подробно проанализируем ошибки, с которыми столкнулся автор темы, выясним их истинные причины и построим надежное, оптимальное решение для автоматического заполнения кода печати ярлыка в документе «Отчет производства за смену».

Разбор критических ошибок разработчика

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

1. Ошибка нехватки фактических параметров

В самом первом сообщении автор вызывает функцию общего модуля следующим образом:

КодПечати=РаботаСоСправочниками.КодПечати(КодПечати);

При этом сама функция в общем модуле объявлена как КодПечати(Артикул, ЭлементНоменклатуры). То есть функция ожидает на вход два обязательных параметра (артикул и ссылку на номенклатуру), а автор передает только один. В результате платформа 1С закономерно генерирует исключение: «Недостаточно фактических параметров». Для исправления этой ошибки всегда контролируйте количество и состав передаваемых аргументов.

2. Использование переменной Объект в обычных формах

Попытка обратиться к реквизитам через конструкцию Объект.Артикул или Объект.Продукция привела к ошибке: «Значение не является значением объектного типа». Почему так произошло?

Переменная Объект является предопределенным основным реквизитом в управляемых формах (платформа 8.3). В обычных формах (толстый клиент) такой переменной по умолчанию не существует. Весь контекст документа в модуле обычной формы доступен напрямую по именам его реквизитов или через ключевое слово ЭтотОбъект. Таким образом, обращаться к табличной части нужно было как Продукция или ЭтотОбъект.Продукция, а не через несуществующий Объект.

3. Ошибочный доступ к данным через визуальные элементы формы

В попытках решить проблему автор написал код:


КодПечати=РаботаСоСправочниками.КодПечати(ЭлементыФормы.Продукция.Артикул,КодПечати);

Эта строка вызвала ошибку: «Поле объекта не обнаружено (Артикул)». Дело в том, что ЭлементыФормы.Продукция — это элемент управления типа «Табличное поле», предназначенный исключительно для визуального отображения данных на форме. У самого табличного поля нет свойства Артикул. Чтобы получить данные строки, с которой в данный момент работает пользователь, необходимо обращаться к свойству ТекущиеДанные этого элемента управления.

4. Нарушение логики метода ПолучитьПоследнее

В коде функции общего модуля автор написал:


ЗначениеРесурсов=РегистрыСведений.КодПечатиЯрлыка.ПолучитьПоследнее(Артикул,Отбор);

Метод ПолучитьПоследнее() для периодических регистров сведений имеет следующий синтаксис: ПолучитьПоследнее(<Период>, <Отбор>). Первым параметром метод ожидает получить объект типа Дата. Автор же передал туда строковую переменную Артикул. Платформа не сможет выполнить такое обращение. Если срез последних нужен на текущий момент, первым параметром следует передать пустую дату или опустить его вовсе, поставив запятую.

5. Архитектурная ошибка: поиск по строковому артикулу

Использование текстового артикула (тип Строка) в качестве единственного измерения регистра сведений — это крайне неоптимальное решение. Артикулы в базе данных могут дублироваться, содержать невидимые символы (пробелы, переносы) или изменяться со временем. Поиск по строковым полям в базах данных SQL происходит значительно медленнее, чем по уникальным идентификаторам ссылочного типа. Правильным решением будет использовать в качестве измерения регистра ссылку на справочник Номенклатура.

Проектируем правильную архитектуру решения

Чтобы автозаполнение работало быстро, надежно и без ошибок, выполним рефакторинг структуры данных и программного кода.

Шаг 1. Структура регистра сведений

Создадим или скорректируем регистр сведений КодПечатиЯрлыка:

  1. Тип регистра: Непериодический.
  2. Измерение: Номенклатура (тип: СправочникСсылка.Номенклатура).
  3. Ресурс: КодПечати (тип: СправочникСсылка.КодыПечатиЯрлыков).

Шаг 2. Реализация функции в общем модуле

Разместим в общем модуле РаботаСоСправочниками (убедитесь, что у модуля стоят флажки «Клиент» и «Сервер») универсальную функцию для получения кода печати по ссылке на номенклатуру:


Функция ПолучитьКодПечатиНоменклатуры(СсылкаНоменклатуры) Экспорт
    
    Если Не ЗначениеЗаполнено(СсылкаНоменклатуры) Тогда
        Возврат Справочники.КодыПечатиЯрлыков.ПустаяСсылка();
    КонецЕсли;
    
    // Формируем отбор по измерению "Номенклатура"
    Отбор = Новый Структура("Номенклатура", СсылкаНоменклатуры);
    
    // Получаем последнее значение ресурса
    ЗначениеРесурсов = РегистрыСведений.КодПечатиЯрлыка.ПолучитьПоследнее(, Отбор);
    
    Возврат ЗначениеРесурсов.КодПечати;
    
КонецФункции

Интеграция в форму документа «Отчет производства за смену»

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

Откроем модуль формы документа и найдем процедуру-обработчик события при изменении номенклатуры в табличной части (обычно она вызывается из общей процедуры ПродукцияПриИзменении):


Процедура ПродукцияНоменклатураПриИзменении(Элемент)
    
    // Получаем строку табличной части, в которой находится фокус ввода
    СтрокаТабличнойЧасти = ЭлементыФормы.Продукция.ТекущиеДанные;
    
    // Обязательно проверяем, что строка спозиционирована корректно
    Если СтрокаТабличнойЧасти = Неопределено Тогда
        Возврат;
    КонецЕсли;
    
    // Стандартные обработчики пересчета цен, счетов учета и себестоимости
    ОбработкаТабличныхЧастей.ПриИзмененииНоменклатурыТабЧасти(СтрокаТабличнойЧасти, ЭтотОбъект);
    ПриИзмененииПродукции(СтрокаТабличнойЧасти, "Продукция");
    Ценообразование.ЗаполнитьПлановуюСебестоимость(СтрокаТабличнойЧасти, Дата);
    ОбработкаТабличныхЧастей.ПересчитатьПлановуюСумму(СтрокаТабличнойЧасти);
    ЗаполнитьСчетаУчетаВСтрокеТабЧасти(СтрокаТабличнойЧасти, "Продукция", Истина);
    ЗаполнитьНоменклатурнуюГруппу(СтрокаТабличнойЧасти);
    
    // НАШ БЛОК: Автоматическое заполнение Кода печати
    ТекущаяНоменклатура = СтрокаТабличнойЧасти.Номенклатура;
    
    Если ЗначениеЗаполнено(ТекущаяНоменклатура) Тогда
        // Получаем код печати через наш общий модуль
        СтрокаТабличнойЧасти.КодПечати = РаботаСоСправочниками.ПолучитьКодПечатиНоменклатуры(ТекущаяНоменклатура);
    Иначе
        // Если номенклатура была очищена, очищаем и код печати
        СтрокаТабличнойЧасти.КодПечати = Справочники.КодыПечатиЯрлыков.ПустаяСсылка();
    КонецЕсли;
    
КонецПроцедуры

Как избежать проблем с производительностью (Антипаттерн «Запрос в цикле»)

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

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


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

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

Заключение

Мы детально проанализировали процесс настройки автозаполнения кодов печати в документе «Отчет производства за смену». Главный вывод, который необходимо сделать: всегда разделяйте контекст управляемых и обычных форм, корректно работайте с типами данных при обращении к методам платформы (таким как ПолучитьПоследнее) и избегайте необоснованных запросов к СУБД внутри циклов. Следуя этим простым правилам, вы получите стабильно работающее прикладное решение, которое сэкономит время пользователей и сохранит высокую производительность информационной базы.

← На главную