Почему возникает ошибка «Значение не является значением объектного типа (Добавить)» и как правильно организовать работу с данными в форме 1С

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

В процессе разработки на платформе 1С:Предприятие 8.3 начинающие программисты часто сталкиваются с ошибкой Значение не является значением объектного типа. Эта ошибка — своего рода «сигнал» от платформы, сообщающий о том, что вы пытаетесь обратиться к методу или свойству переменной, тип которой этого не поддерживает. Рассмотрим детально ситуацию, возникшую в коде автора, проанализируем архитектурные ошибки и выработаем правильный подход к решению задачи.

Разбор основной причины ошибки: путаница в типах данных

Проанализируем строку кода, на которой «спотыкается» программа: ТекущийНаряд.Количество2.Добавить(ТекущийНаряд.Исполнители.Количество());. Выясним причину, почему платформа выдает ошибку именно здесь.

В языке 1С метод Добавить() существует только у коллекций. К ним относятся:

В данном случае Количество2 — это, судя по логике задачи, обычный числовой реквизит документа или формы, предназначенный для хранения общего числа строк в таблице. У типа данных Число нет и не может быть метода Добавить(). Когда мы пишем Количество2.Добавить(...), система интерпретирует Количество2 как примитивное значение (например, 5 или 0), а у числа нет методов, оно не является объектом в контексте объектно-ориентированного доступа к методам коллекций.

Как исправить: Если нам нужно записать результат подсчета в числовое поле, мы должны использовать оператор присваивания:

ТекущийНаряд.Количество2 = ТекущийНаряд.Исполнители.Количество();

Анализ работы с контекстом: ошибки серверных вызовов

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

Автор использует следующую конструкцию:


ЭтотОбъект.Прочитать();
ДокОбъект = РеквизитФормыВЗначение("ТекущийНаряд");
ЗначениеВРеквизитФормы(ДокОбъект, "ТекущийНаряд");
ЭтотОбъект.Прочитать();

Посмотрим, почему это делать категорически нельзя:

  1. Метод Прочитать(): Этот метод принудительно считывает данные из базы данных, затирая всё, что пользователь ввел на форме. Если бухгалтер или менеджер заполнил 10 полей и после изменения строки в таблице сработал этот код — все введенные, но еще не записанные в базу данные, будут безвозвратно удалены.
  2. Связка РеквизитФормыВЗначение и ЗначениеВРеквизитФормы: Эти функции служат для преобразования данных формы (которые являются специальными объектами типа ДанныеФормыСтруктура) в полноценные прикладные объекты (например, ДокументОбъект). Это «тяжелая» операция, требующая выделения памяти на сервере для создания копии всего документа. В коде автора между этими двумя вызовами ничего не происходит с переменной ДокОбъект. Это означает, что сервер выполняет огромную бесполезную работу, просто перегоняя данные из одного формата в другой и обратно.
  3. Двойной вызов Прочитать(): Это дублирование действий, которое не несет никакой логической нагрузки, но создает дополнительную очередь запросов к СУБД.

Как правильно организовать подсчет строк в табличной части

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

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


&НаКлиенте
Процедура ТекущийНарядИсполнителиПриИзменении(Элемент)
    // Прямое обращение к данным формы на клиенте
    ТекущийНаряд.Количество2 = ТекущийНаряд.Исполнители.Количество();
    
    // Платформа сама поймет, что данные изменились, 
    // и обновит поле на экране без вызова ОбновитьОтображениеДанных()
КонецПроцедуры

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

Управление модифицированностью формы

Проанализируем еще одну деталь: НужноПерезаписатьНаряд = Истина;. Автор пытается вручную отследить, нужно ли сохранять документ. В 1С для этого есть встроенный механизм.

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

ЭтаФорма.Модифицированность = Истина;

Это гарантирует, что при попытке закрыть форму без сохранения, 1С выдаст стандартное предупреждение: «Данные были изменены. Сохранить изменения?». Использование самописных флагов вроде НужноПерезаписатьНаряд лишает вас этой стандартной и очень важной функциональности.

Итоговый правильный код

С учетом всего вышесказанного, приведем код к профессиональному виду. Предположим, что ТекущийНаряд — это основной реквизит формы (тип ДокументОбъект или СправочникОбъект).


&НаКлиенте
Процедура ТекущийНарядИсполнителиПриИзменении(Элемент)
    
    // 1. Выполняем расчет прямо на клиенте - это быстро и эффективно
    КоличествоСтрок = ТекущийНаряд.Исполнители.Количество();
    
    // 2. Проверяем, изменилось ли значение, чтобы не дергать форму зря
    Если ТекущийНаряд.Количество2 <> КоличествоСтрок Тогда
        ТекущийНаряд.Количество2 = КоличествоСтрок;
        // Модифицированность установится автоматически при изменении реквизита
    КонецЕсли;
    
КонецПроцедуры

Когда действительно нужны серверные вызовы?

Рассмотрим ситуации, когда нам все-таки пришлось бы идти на сервер (но не так, как в исходном примере):

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


&НаКлиенте
Процедура ТекущийНарядИсполнителиПриИзменении(Элемент)
    ТекущийНаряд.Количество2 = ПолучитьКоличествоНаСервере();
КонецПроцедуры

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

Важный момент: использование директивы &НаСервереБезКонтекста является наиболее предпочтительным, так как при таком вызове на сервер не передаются все данные формы, что значительно экономит трафик и время отклика системы.

Резюме для запоминания

  1. Ошибка «не является значением объектного типа» в 90% случаев означает, что вы вызвали метод (через точку) у переменной, которая содержит число, строку или Неопределено.
  2. Никогда не используйте Прочитать() в обработчиках событий изменения данных на форме, если не хотите стереть работу пользователя.
  3. РеквизитФормыВЗначение нужен только тогда, когда вам необходимо вызвать экспортный метод из модуля объекта или выполнить ДокументОбъект.Записать(). В остальных случаях работайте напрямую с реквизитами формы.
  4. Старайтесь считать на клиенте все, что можно посчитать без обращения к базе данных. Это золотое правило производительности управляемых форм.
← На главную