Как запретить изменение организации или валюты в договоре при наличии документов и движений?

Программист 1С v8.3 (Обычные формы) Управленческий учет
← На главную

В процессе сопровождения конфигураций «1С:Предприятие 8» (особенно таких как УТ 10.3, УПП 1.3 или сильно кастомизированных систем на обычных формах) часто возникает задача обеспечения целостности данных. Одной из критически важных задач является запрет редактирования ключевых реквизитов в справочнике ДоговорыКонтрагентов, если по этому договору уже проведены документы или зафиксированы движения в регистрах. Изменение организации или валюты в таком случае приведет к «развалу» партионного учета и неверным взаиморасчетам.

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

Место реализации проверки: Модуль формы или Модуль объекта?

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

  1. Модуль формы: Позволяет заблокировать поля на интерфейсном уровне. Это удобно для пользователя, так как он сразу видит, что поле недоступно. Однако этот метод не защищает базу от программного изменения объекта (например, через внешнюю обработку).
  2. Модуль объекта: Процедура ПередЗаписью в модуле объекта выполняется всегда, независимо от того, меняется договор вручную в форме или программно. Это гарантирует 100% защиту данных.

Рекомендация: Лучше всего комбинировать оба подхода. В модуле формы в обработчике ПриОткрытии или ПриЧтенииНаСервере мы визуально блокируем поля, а в модуле объекта в процедуре ПередЗаписью выполняем финальную проверку перед записью в базу данных.

Способ 1. Использование стандартного механизма (Критерии отбора)

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


Процедура ПередЗаписью(Отказ)
    // Проверка осуществляется только если записывается уже существующий договор
    Если НЕ ОбменДанными.Загрузка И НЕ ЭтоНовый() Тогда
        
        // Проверяем, изменились ли критичные реквизиты
        Если Организация <> Ссылка.Организация ИЛИ ВалютаВзаиморасчетов <> Ссылка.ВалютаВзаиморасчетов Тогда
            
            Запрос = Новый Запрос;
            Запрос.Текст = 
                "ВЫБРАТЬ ПЕРВЫЕ 1
                |	ДокументыПоДоговору.Ссылка
                |ИЗ
                |	КритерийОтбора.ДокументыПоДоговоруКонтрагента(&Договор) КАК ДокументыПоДоговору";
            
            Запрос.УстановитьПараметр("Договор", Ссылка);
            Результат = Запрос.Выполнить();
            
            Если НЕ Результат.Пустой() Тогда
                Сообщить("По договору уже оформлены документы. Изменение организации или валюты запрещено!", СтатусСообщения.Важное);
                Отказ = Истина;
            КонецЕсли;
        КонецЕсли;
        
    КонецЕсли;
КонецПроцедуры

Обратите внимание на проверку ОбменДанными.Загрузка. Это критически важно: если данные загружаются из другой базы (например, при синхронизации), проверка не должна блокировать процесс, иначе обмен прервется с ошибкой.

Способ 2. Проверка по регистрам накопления (Поиск «движений»)

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


Функция СуществуютДвиженияПоДоговору(ДоговорСсылка)
    Запрос = Новый Запрос;
    Запрос.УстановитьПараметр("ДоговорКонтрагента", ДоговорСсылка);
    Запрос.Текст = 
    "ВЫБРАТЬ ПЕРВЫЕ 1 ДоговорКонтрагента ИЗ РегистрНакопления.ВзаиморасчетыСКонтрагентами ГДЕ ДоговорКонтрагента = &ДоговорКонтрагента
    |ОБЪЕДИНИТЬ ВСЕ
    |ВЫБРАТЬ ПЕРВЫЕ 1 ДоговорКонтрагента ИЗ РегистрНакопления.РасчетыСКонтрагентами ГДЕ ДоговорКонтрагента = &ДоговорКонтрагента
    |ОБЪЕДИНИТЬ ВСЕ
    |ВЫБРАТЬ ПЕРВЫЕ 1 ДоговорКонтрагента ИЗ РегистрНакопления.Продажи ГДЕ ДоговорКонтрагента = &ДоговорКонтрагента";
    
    Возврат НЕ Запрос.Выполнить().Пустой();
КонецФункции

Преимущества этого метода: Скорость. Запрос к конкретным таблицам регистров работает быстрее, чем поиск по всей базе. Вы сами определяете список регистров, которые считаются «критичными».

Способ 3. Универсальный метод «НайтиПоСсылкам»

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


МассивСсылок = Новый Массив;
МассивСсылок.Добавить(Ссылка);
ТаблицаСсылок = НайтиПоСсылкам(МассивСсылок);

Если ТаблицаСсылок.Количество() > 0 Тогда
    // Объект где-то используется
    Для Каждого СтрокаСсылок Из ТаблицаСсылок Цикл
        Сообщить("На договор ссылается объект: " + СтрокаСсылок.Данные);
    КонецЦикла;
    Отказ = Истина;
КонецЕсли;

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

Важный нюанс: Привилегированный режим

Выясним причину, по которой проверка может не сработать. Если у текущего пользователя ограничены права (например, настроены RLS — ограничения на уровне записей), запрос может не увидеть документы другого подразделения или организации. В результате пользователь сможет изменить договор, по которому «не видит» движений, что приведет к ошибке в учете.

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

Современный подход (Библиотека стандартных подсистем)

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

Итоги

Для надежного запрета изменения организации в договоре выполните следующие шаги:

  1. Определите, что именно считать «движением»: простое наличие ссылки в любом документе или наличие записей в регистрах.
  2. Используйте процедуру ПередЗаписью в модуле объекта для контроля изменений.
  3. Всегда проверяйте ОбменДанными.Загрузка, чтобы не блокировать интеграции.
  4. Для повышения производительности используйте прямые запросы к Критериям отбора или ключевым Регистрам накопления.
  5. Выполняйте код в привилегированном режиме, чтобы избежать ошибок, связанных с правами доступа.

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

← На главную