В практике разработки на платформе 1С:Предприятие, особенно в конфигурациях вроде "Управление торговлей 11", часто возникает задача защиты данных от случайного или намеренного изменения. Типичный сценарий: менеджер работает с "Заказом клиента", часть позиций уже отгружена или обеспечена, и нам необходимо жестко запретить редактирование этих конкретных строк, оставив остальные доступными для работы.
Давайте подробно разберем, как реализовать такую защиту комплексно. Мы проанализируем решения на уровне интерфейса (для удобства пользователя) и на уровне объекта (для надежности данных), а также выясним, почему некоторые очевидные методы могут не сработать.
Чтобы защита была эффективной и эргономичной, нам нужно действовать на двух уровнях:
Первое, что мы должны сделать — дать пользователю понять, какие строки недоступны. Для этого идеально подходит механизм УсловноеОформление формы. При необходимости можно воспользоваться готовым расширением для окраски строк по группам номенклатуры в УТ 11.
Настроим условное оформление динамически при создании формы или статически в конфигураторе. Рассмотрим логику действий:
ВариантОбеспечения равно значению "Отгрузить" (или "Отгружено").ТолькоПросмотр в значение Истина. Дополнительно можно изменить ЦветФона на серый, чтобы визуально выделить заблокированные строки.Это решение улучшает User Experience (UX), но не гарантирует безопасность. Пользователь не сможет кликнуть в ячейку, но кнопки командной панели (например, "Заполнить по...", "Обеспечить") могут программно изменить данные в этих строках, игнорируя настройки формы.
Чтобы предотвратить попытки удаления или начала редактирования (если условное оформление не сработало или нужно вывести предупреждение), используем события элементов формы.
Нам понадобятся события таблицы формы: ПередНачаломИзменения и ПередУдалением.
Пример кода для модуля формы:
&НаКлиенте
Процедура ТоварыПередНачаломИзменения(Элемент, Отказ)
ТекущиеДанные = Элемент.ТекущиеДанные;
Если ТекущиеДанные <> Неопределено Тогда
// Проверяем условие блокировки, например, статус строки
Если ТекущиеДанные.ВариантОбеспечения = ПредопределенноеЗначение("Перечисление.ВариантыОбеспечения.Отгрузить") Тогда
Отказ = Истина;
ПоказатьОповещениеПользователя("Изменение запрещено", , "Строка уже отгружена или находится в обеспечении!");
КонецЕсли;
КонецЕсли;
КонецПроцедуры
&НаКлиенте
Процедура ТоварыПередУдалением(Элемент, Отказ, СписокСтрок)
// Перебираем удаляемые строки (их может быть несколько при множественном выделении)
Для Каждого ИдентификаторСтроки Из СписокСтрок Цикл
СтрокаТЧ = Элемент.ДанныеСтроки(ИдентификаторСтроки);
Если СтрокаТЧ.ВариантОбеспечения = ПредопределенноеЗначение("Перечисление.ВариантыОбеспечения.Отгрузить") Тогда
Отказ = Истина;
Сообщение = Новый СообщениеПользователю;
Сообщение.Текст = "Нельзя удалять отгруженные строки!";
Сообщение.Сообщить();
Прервать; // Достаточно найти одну запрещенную, чтобы отменить все удаление
КонецЕсли;
КонецЦикла;
КонецПроцедуры
Важное ограничение: Эти события срабатывают только при действиях пользователя "мышкой" или клавиатурой. Если пользователь нажмет кнопку "Заполнить -> Пересчитать цены", данные события не вызовутся, и защищенные строки будут изменены.
Это самый надежный способ. Логика заключается в следующем: в момент записи документа (событие ПередЗаписью в модуле объекта) мы должны сравнить то, что сейчас находится в памяти (измененный объект), с тем, что записано в базе данных.
В теме на форуме предлагалось использовать хеширование строк (сериализовать строку в XML/строку и считать хеш). Рассмотрим, почему этот метод не идеален:
Более правильный подход — прямое сравнение данных через запрос. Мы должны получить "старое" состояние документа из базы данных и проверить, не исчезли ли или не изменились ли критичные строки.
Разберем алгоритм проверки в событии ПередЗаписью:
ЭтоНовый()), проверку можно пропустить (так как защищать еще нечего).Ссулка.ГДЕ ВариантОбеспечения = &Отгрузить).Сопоставление строк — тонкий момент. В 1С строки табличных частей имеют КлючСвязи (в некоторых конфигурациях) или НомерСтроки. Однако НомерСтроки может меняться при удалении/вставке строк в середину. Самый надежный вариант для УТ 11 — использовать КлючСвязи (если он ведется) или комбинацию ключевых полей, если мы допускаем перемещение строк.
Рассмотрим пример кода проверки целостности защищенных строк:
Процедура ПередЗаписью(Отказ, РежимЗаписи, РежимПроведения)
Если ОбменДанными.Загрузка Или ЭтоНовый() Тогда
Возврат;
КонецЕсли;
// 1. Получаем эталонные данные из базы - то, что НЕЛЬЗЯ менять
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ЗаказКлиентаТовары.КлючСвязи КАК КлючСвязи,
| ЗаказКлиентаТовары.Номенклатура КАК Номенклатура,
| ЗаказКлиентаТовары.Количество КАК Количество,
| ЗаказКлиентаТовары.НомерСтроки КАК НомерСтроки
|ИЗ
| Документ.ЗаказКлиента.Товары КАК ЗаказКлиентаТовары
|ГДЕ
| ЗаказКлиентаТовары.Ссылка = &Ссылка
| И ЗаказКлиентаТовары.ВариантОбеспечения = ЗНАЧЕНИЕ(Перечисление.ВариантыОбеспечения.Отгрузить)";
Запрос.УстановитьПараметр("Ссылка", Ссылка);
ТаблицаЗапрещенных = Запрос.Выполнить().Выгрузить();
ТаблицаЗапрещенных.Индексы.Добавить("КлючСвязи"); // Для быстрого поиска
// 2. Проверяем текущую ТЧ
Для Каждого ЭталоннаяСтрока Из ТаблицаЗапрещенных Цикл
// Попытка найти строку в текущем документе по ключу связи
// Если КлючаСвязи нет, придется искать по Номенклатуре+Характеристике и т.д., что сложнее
НайденныеСтроки = Товары.НайтиСтроки(Новый Структура("КлючСвязи", ЭталоннаяСтрока.КлючСвязи));
Если НайденныеСтроки.Количество() = 0 Тогда
// ОШИБКА: Строка была удалена!
Отказ = Истина;
Сообщение = Новый СообщениеПользователю;
Сообщение.Текст = СтрШаблон("Ошибка! Удалена защищенная строка №%1 с товаром %2",
ЭталоннаяСтрока.НомерСтроки, ЭталоннаяСтрока.Номенклатура);
Сообщение.Сообщить();
Иначе
ТекущаяСтрока = НайденныеСтроки[0];
// 3. Проверяем, не изменились ли данные
Если ТекущаяСтрока.Номенклатура <> ЭталоннаяСтрока.Номенклатура
Или ТекущаяСтрока.Количество <> ЭталоннаяСтрока.Количество Тогда
Отказ = Истина;
Сообщение = Новый СообщениеПользователю;
Сообщение.Текст = СтрШаблон("Ошибка! Изменены данные в защищенной строке (было №%1). Товар уже отгружен/обеспечен.",
ЭталоннаяСтрока.НомерСтроки);
Сообщение.Сообщить();
КонецЕсли;
КонецЕсли;
КонецЦикла;
КонецПроцедуры
Работая с конфигурацией "Управление торговлей 11", стоит учитывать следующие моменты:
КлючСвязи. Он уникален в пределах документа и идеален для идентификации строки. Опирайтесь на него.ПередЗаписью нужно добавить исключение: разрешать изменение, если оно инициировано системным процессом (обычно проверяют ДополнительныеСвойства документа, куда программно пишут флаги типа "РазрешитьРедактирование").&Перед("ПередЗаписью") или &После("ПередЗаписью"). Это позволит обновлять конфигурацию без проблем.Для полноценного решения задачи:
ПередНачаломИзменения), чтобы предупреждать пользователя вежливо и сразу.ПередЗаписью) через сравнение с данными базы по КлючСвязи, чтобы защититься от "волшебных кнопок" и групповых изменений.