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