В процессе разработки на платформе 1С:Предприятие 8.3 начинающие программисты часто сталкиваются с ошибкой Значение не является значением объектного типа. Эта ошибка — своего рода «сигнал» от платформы, сообщающий о том, что вы пытаетесь обратиться к методу или свойству переменной, тип которой этого не поддерживает. Рассмотрим детально ситуацию, возникшую в коде автора, проанализируем архитектурные ошибки и выработаем правильный подход к решению задачи.
Проанализируем строку кода, на которой «спотыкается» программа: ТекущийНаряд.Количество2.Добавить(ТекущийНаряд.Исполнители.Количество());. Выясним причину, почему платформа выдает ошибку именно здесь.
В языке 1С метод Добавить() существует только у коллекций. К ним относятся:
В данном случае Количество2 — это, судя по логике задачи, обычный числовой реквизит документа или формы, предназначенный для хранения общего числа строк в таблице. У типа данных Число нет и не может быть метода Добавить(). Когда мы пишем Количество2.Добавить(...), система интерпретирует Количество2 как примитивное значение (например, 5 или 0), а у числа нет методов, оно не является объектом в контексте объектно-ориентированного доступа к методам коллекций.
Как исправить: Если нам нужно записать результат подсчета в числовое поле, мы должны использовать оператор присваивания:
ТекущийНаряд.Количество2 = ТекущийНаряд.Исполнители.Количество();
Рассмотрим более глубокие проблемы в представленном фрагменте кода, которые могут привести к потере данных и низкой производительности системы. Проанализируем процедуру ТекущийНарядИсполнителиПриИзмененииНаСервере().
Автор использует следующую конструкцию:
ЭтотОбъект.Прочитать();
ДокОбъект = РеквизитФормыВЗначение("ТекущийНаряд");
ЗначениеВРеквизитФормы(ДокОбъект, "ТекущийНаряд");
ЭтотОбъект.Прочитать();
Посмотрим, почему это делать категорически нельзя:
ДанныеФормыСтруктура) в полноценные прикладные объекты (например, ДокументОбъект). Это «тяжелая» операция, требующая выделения памяти на сервере для создания копии всего документа. В коде автора между этими двумя вызовами ничего не происходит с переменной ДокОбъект. Это означает, что сервер выполняет огромную бесполезную работу, просто перегоняя данные из одного формата в другой и обратно.Выясним, как реализовать задачу обновления количества исполнителей без лишних затрат ресурсов. Для подсчета количества строк в таблице Исполнители и записи этого числа в поле Количество2 вообще не обязательно обращаться к серверу.
Рассмотрим оптимальный вариант реализации на клиенте:
&НаКлиенте
Процедура ТекущийНарядИсполнителиПриИзменении(Элемент)
// Прямое обращение к данным формы на клиенте
ТекущийНаряд.Количество2 = ТекущийНаряд.Исполнители.Количество();
// Платформа сама поймет, что данные изменились,
// и обновит поле на экране без вызова ОбновитьОтображениеДанных()
КонецПроцедуры
В управляемом приложении 1С объекты типа ДанныеФормыКоллекция (которым является ваша таблица на форме) имеют метод Количество(), доступный на клиенте. Это позволяет избежать «походов» на сервер, что делает интерфейс программы мгновенным для пользователя.
Проанализируем еще одну деталь: НужноПерезаписатьНаряд = Истина;. Автор пытается вручную отследить, нужно ли сохранять документ. В 1С для этого есть встроенный механизм.
Когда мы меняем данные в реквизитах, связанных с объектом, платформа сама устанавливает флаг модифицированности. Если же мы хотим программно указать, что форма изменилась, следует использовать стандартное свойство:
ЭтаФорма.Модифицированность = Истина;
Это гарантирует, что при попытке закрыть форму без сохранения, 1С выдаст стандартное предупреждение: «Данные были изменены. Сохранить изменения?». Использование самописных флагов вроде НужноПерезаписатьНаряд лишает вас этой стандартной и очень важной функциональности.
С учетом всего вышесказанного, приведем код к профессиональному виду. Предположим, что ТекущийНаряд — это основной реквизит формы (тип ДокументОбъект или СправочникОбъект).
&НаКлиенте
Процедура ТекущийНарядИсполнителиПриИзменении(Элемент)
// 1. Выполняем расчет прямо на клиенте - это быстро и эффективно
КоличествоСтрок = ТекущийНаряд.Исполнители.Количество();
// 2. Проверяем, изменилось ли значение, чтобы не дергать форму зря
Если ТекущийНаряд.Количество2 <> КоличествоСтрок Тогда
ТекущийНаряд.Количество2 = КоличествоСтрок;
// Модифицированность установится автоматически при изменении реквизита
КонецЕсли;
КонецПроцедуры
Рассмотрим ситуации, когда нам все-таки пришлось бы идти на сервер (но не так, как в исходном примере):
Количество2 требуется сложный запрос к другим таблицам базы данных.В таких случаях процедура &НаСервере должна только возвращать результат вычислений, а не манипулировать объектом через Прочитать(). Пример грамотного серверного вызова:
&НаКлиенте
Процедура ТекущийНарядИсполнителиПриИзменении(Элемент)
ТекущийНаряд.Количество2 = ПолучитьКоличествоНаСервере();
КонецПроцедуры
&НаСервереБезКонтекста
Функция ПолучитьКоличествоНаСервере()
// Здесь может быть сложный запрос к БД
Возврат РезультатСложногоРасчета;
КонецФункции
Важный момент: использование директивы &НаСервереБезКонтекста является наиболее предпочтительным, так как при таком вызове на сервер не передаются все данные формы, что значительно экономит трафик и время отклика системы.
Неопределено.ДокументОбъект.Записать(). В остальных случаях работайте напрямую с реквизитами формы.