Почему не работает код в ПриОкончанииРедактирования в 1С и как это сделать правильно?

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

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

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

Анализ проблемы: почему код в событии ПриОкончанииРедактирования не срабатывает?

Рассмотрим типичный ошибочный код, который часто пытаются использовать начинающие разработчики:


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

Давайте проанализируем эту ситуацию и выявим сразу несколько критических ошибок, заложенных в данном подходе:

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

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

Жизненный цикл редактирования строки в списке

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

  1. ПередНачаломРедактирования — срабатывает до того, как строка перейдет в режим редактирования. Здесь можно отменить процесс.
  2. ПриНачалеРедактирования — строка открылась для ввода данных.
  3. ПередОкончаниемРедактирования — пользователь закончил редактирование (нажал Enter или кликнул мышкой на другую строку), но данные еще не записаны в базу данных и сама строка формы все еще находится в режиме изменения. На этом этапе коллекция ТекущиеДанные полностью открыта для записи! Платформа еще не зафиксировала изменения.
  4. ПриОкончанииРедактирования — процесс интерактивного редактирования завершен. Изменения строки уже переданы платформой в базу данных (или отменены), режим редактирования закрыт. На этом этапе коллекция ТекущиеДанные переходит в режим "только для чтения". Любая попытка изменить реквизиты строки здесь вызовет ошибку. Это событие предназначено исключительно для постобработки (например, для оповещения других форм или визуального обновления интерфейса).

Таким образом, логику изменения данных строки в форме списка необходимо переносить на этап, когда редактирование еще не завершено окончательно.

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

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

Рассмотрим пример реализации:


Процедура ИзображенияПередОкончаниемРедактирования(Элемент, НоваяСтрока, ОтменаРедактирования, Отказ)
    
    // Если пользователь отменил редактирование (например, нажал Esc), ничего делать не нужно
    Если ОтменаРедактирования Тогда
        Возврат;
    КонецЕсли;
    
    ТекущиеДанныеСтроки = Элемент.ТекущиеДанные;
    
    Если ТекущиеДанныеСтроки <> Неопределено Тогда
        // На этапе ПередОкончаниемРедактирования поля ТекущихДанных доступны для изменения
        Суффикс = " " + ИмяПользователя();
        ТекущиеДанныеСтроки.Наименование = СокрЛП(ТекущиеДанныеСтроки.Наименование) + Суффикс;
    КонецЕсли;
    
КонецПроцедуры

Важное преимущество этого способа: нам не нужно беспокоиться о транзакциях и вызове метода Записать(). Платформа выполнит запись автоматически и безопасно.

Решение 2: Архитектурно правильный подход (на уровне Модуля объекта)

Хотя решение на уровне формы списка является рабочим, оно имеет серьезные архитектурные недостатки с точки зрения методологии разработки на платформе 1С:

Чтобы обеспечить гарантированное заполнение реквизитов при любых обстоятельствах, бизнес-логику автозаполнения необходимо переносить в Модуль объекта справочника в обработчик события ПередЗаписью.

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


Процедура ПередЗаписью(Отказ)
    
    // Выполняем автоподстановку только для новых элементов справочника
    Если ЭтоНовый() Тогда
        
        ИмяТекущегоПользователя = ИмяПользователя();
        Суффикс = " " + ИмяТекущегоПользователя;
        
        // Получаем максимальную длину наименования из метаданных этого справочника
        МаксДлинаНаименования = Метаданные().ДлинаНаименования;
        
        // Обрабатываем ситуацию, когда наименование изначально пустое
        Если ПустаяСтрока(Наименование) Тогда
            Наименование = "Изображение";
        КонецЕсли;
        
        // Проверяем, помещается ли суффикс в ограничение длины реквизита
        Если СтрДлина(Наименование + Суффикс) > МаксДлинаНаименования Тогда
            // Безопасно обрезаем наименование слева, чтобы вошел наш суффикс
            Наименование = Лев(Наименование, МаксДлинаНаименования - СтрДлина(Суффикс)) + Суффикс;
        Иначе
            Наименование = Наименование + Суффикс;
        КонецЕсли;
        
    КонецЕсли;
    
КонецПроцедуры

Сравнительный анализ решений

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

Критерий сравнения Решение 1 (ПередОкончаниемРедактирования в форме) Решение 2 (ПередЗаписью в модуле объекта)
Где выполняется код На клиенте (в контексте формы списка) На сервере (при записи в базу данных)
Программное создание элементов Не обрабатывается Обрабатывается автоматически
Зависимость от интерфейса Сработает только при интерактивном вводе в конкретном списке Не зависит от интерфейса
Сложность реализации Низкая (буквально пара строк кода) Низкая, но требует контроля ограничений метаданных

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

← На главную