Как запретить редактирование ранее сохраненных строк в табличной части документа?

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

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

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

Способ 1. Использование скрытого реквизита-флага и событий таблицы формы

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

Разберем пошаговый алгоритм настройки:

  1. В дереве метаданных добавляем в табличную часть реквизит Заблокирована с типом Булево.
  2. В модуле объекта в обработчике ПередЗаписью устанавливаем признак для всех строк табличной части:

Процедура ПередЗаписью(Отказ, РежимЗаписи, РежимПроведения)
	
	Для Каждого Стр Из Сообщения Цикл
		Стр.Заблокирована = Истина;
	КонецЦикла;
	
КонецПроцедуры

Теперь перейдем в форму документа. Чтобы заблокировать изменение и удаление строк на клиенте, создадим обработчики событий для элемента управления таблицы на форме:

  1. Для таблицы формы назначаем событие ПередНачаломИзменения.
  2. Для этой же таблицы назначаем событие ПередУдалением.
  3. Для корректной обработки копирования строк используем событие ПередНачаломДобавления.

Посмотрим на пример клиентского кода формы:


&НаКлиенте
Процедура СообщенияПередНачаломИзменения(Элемент, Отказ)
	
	ТекущиеДанные = Элемент.ТекущиеДанные;
	Если ТекущиеДанные <> Неопределено И ТекущиеДанные.Заблокирована Тогда
		Отказ = Истина;
	КонецЕсли;
	
КонецПроцедуры

&НаКлиенте
Процедура СообщенияПередУдалением(Элемент, Отказ)
	
	ТекущиеДанные = Элемент.ТекущиеДанные;
	Если ТекущиеДанные <> Неопределено И ТекущиеДанные.Заблокирована Тогда
		Отказ = Истина;
	КонецЕсли;
	
КонецПроцедуры

&НаКлиенте
Процедура СообщенияПередНачаломДобавления(Элемент, Отказ, Копирование, Родитель, ЭтоГруппа)
	
	// Если пользователь копирует заблокированную строку, 
	// в новой строке флаг должен быть сброшен
	Если Копирование Тогда
		// Логика добавления отработает штатно, но после вставки
		// реквизит Заблокирована в новой строке примет значение Ложь
	КонецЕсли;
	
КонецПроцедуры

Важный момент: если пользователь может скопировать сохраненную строку через стандартную команду копирования, в событии ПриОкончанииРедактирования или в обработчиках добавления новой строки обязательно сбрасывайте значение Заблокирована = Ложь, иначе скопированная строка унаследует признак блокировки.

Способ 2. Блокировка строк через Условное оформление формы

Вместо ручной отмены событий в коде можно использовать декларативный механизм платформы — Условное оформление формы. Это решение делает ячейки заблокированных строк недоступными для ввода на уровне интерфейса.

Разберем, как программно настроить условное оформление в процедуре ПриСозданииНаСервере:


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

При таком подходе платформа автоматически предотвратит вход в режим редактирования ячеек для сохраненных строк. Однако для полного запрета удаления строк все равно потребуется обработчик ПередУдалением.

Способ 3. Проверка по исходному количеству строк

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

  1. При открытии формы на сервере запоминаем количество уже записанных строк в реквизит формы ИсходноеКоличествоСтрок (тип Число).
  2. Для таблицы формы отключаем возможность ручного изменения порядка строк: свойство ИзменениеПорядкаСтрок = Ложь (или свойство РазрешитьПеремещениеСтрок в палитре свойств).
  3. Все строки, чей номер в табличной части меньше либо равен ИсходноеКоличествоСтрок, считаются сохраненными и блокируются при редактировании.

Посмотрим на пример кода:


&НаСервере
Процедура ПриЧтенииНаСервере(ТекущийОбъект)
	ИсходноеКоличествоСтрок = ТекущийОбъект.Сообщения.Количество();
КонецПроцедуры

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

Ограничение метода: если пользователь удалит новую строку и добавит другую, нумерация может сместиться, поэтому обязательно запрещайте перемещение строк и внимательно контролируйте номер строки при удалении.

Способ 4. Разделение интерфейса: история и ввод нового сообщения

Если задача заключается в создании механизма чата, журнала действий или комментариев, лучшим решением с точки зрения UX будет разделение интерфейса на две области:

  1. Верхняя область (История): таблица, отображающая табличную часть документа в режиме ТолькоПросмотр = Истина.
  2. Нижняя область (Ввод): отдельное многострочное поле ввода или компактная временная таблица (данные формы) для набора нового текста с кнопкой «Добавить» / «Отправить».

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

Надежная серверная защита в модуле объекта

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

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


Процедура ПередЗаписью(Отказ, РежимЗаписи, РежимПроведения)
	
	Если Не ЭтоНовый() Тогда
		
		// Считываем исходные строки из базы данных
		ТаблицаВБазе = Ссылка.Сообщения.Выгрузить();
		
		// Проверяем, что ранее существовавшие строки не были удалены или изменены
		Для Индекс = 0 По ТаблицаВБазе.Количество() - 1 Цикл
			
			СтрокаБД = ТаблицаВБазе[Индекс];
			
			// Проверяем наличие строки в текущем объекте
			Если Индекс >= ЭтотОбъект.Сообщения.Количество() Тогда
				Отказ = Истина;
				ВызватьИсключение "Запрещено удалять ранее записанные строки документа!";
			КонецЕсли;
			
			СтрокаТекущая = ЭтотОбъект.Сообщения[Индекс];
			
			// Сравниваем значимые поля
			Если СтрокаТекущая.Текст <> СтрокаБД.Текст Или СтрокаТекущая.Автор <> СтрокаБД.Автор Тогда
				Отказ = Истина;
				ВызватьИсключение "Запрещено изменять текст ранее сохраненных строк!";
			КонецЕсли;
			
		КонецЦикла;
		
	КонецЕсли;
	
КонецПроцедуры

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

Если документ используется в качестве чата несколькими пользователями одновременно, вы неизбежно столкнетесь с оптимистической блокировкой версии объекта: когда один пользователь записывает документ, у второго форма устаревает (ошибка «Данные были изменены или удалены другим пользователем»).

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

← На главную