В процессе автоматизации учета перед разработчиками и администраторами систем 1С часто возникает задача ограничения прав пользователей на ввод документов или изменение дат «задним числом». Ввод данных прошедшим числом может привести к серьезным искажениям в учете: нарушается последовательность проведения документов, «слетает» себестоимость, искажаются остатки на складах и взаиморасчеты с контрагентами, а также усложняется сдача регламентированной отчетности.
В этой статье мы подробно разберем все возможные варианты решения этой задачи: от использования стандартных настроек системы до написания отказоустойчивого программного кода в модулях управляемых форм и объектов.
Для реализации этой логики мы можем пойти несколькими путями в зависимости от архитектуры конфигурации и требований бизнеса. Рассмотрим их по порядку.
Прежде чем приступать к разработке собственных алгоритмов («костылей»), проанализируем возможности стандартного функционала 1С. В типовых конфигурациях, разработанных на базе БСП (Библиотеки Стандартных Подсистем), таких как 1С:ERP, 1С:Комплексная автоматизация, 1С:Управление торговлей или 1С:Бухгалтерия, присутствует мощный и гибкий механизм Даты запрета изменения данных.
Этот механизм позволяет гибко управлять ограничениями без изменения программного кода конфигурации:
Для тонкой настройки прав существует специальная роль Редактирование дат запрета. Если исключить эту роль из профилей полномочий обычных пользователей, они потеряют техническую возможность обходить установленные лимиты.
Если стандартный механизм даты запрета по каким-то причинам не подходит или перед нами стоит задача контролировать заполнение конкретного реквизита формы «на лету», мы можем реализовать проверку непосредственно в модуле управляемой формы.
Рассмотрим простейший, но не самый безопасный вариант проверки при изменении реквизита на клиенте:
&НаКлиенте
Процедура ДатаПриИзменении(Элемент)
Если Объект.Дата < ТекущаяДата() Тогда
Объект.Дата = ТекущаяДата();
ПоказатьПредупреждение(, "Ввод прошедшей даты запрещен! Установлена текущая дата.");
КонецЕсли;
КонецПроцедуры
В чем уязвимость данного метода?
Функция ТекущаяДата(), вызванная на клиенте, возвращает системное время локального компьютера пользователя. Продвинутый пользователь может легко обойти это ограничение: достаточно просто сдвинуть системное время назад на своей операционной системе, и условие проверки перестанет работать корректно. Это критично для локальных рабочих групп без доменной структуры.
Чтобы исключить влияние локальных манипуляций пользователя с часами компьютера, нам необходимо получать эталонное время с сервера. Модернизируем наш код:
&НаКлиенте
Процедура ДатаПриИзменении(Элемент)
АктуальнаяДата = ПолучитьАктуальнуюДатуСервера();
Если Объект.Дата < АктуальнаяДата Тогда
Объект.Дата = АктуальнаяДата;
ПоказатьПредупреждение(, "Нельзя вводить документ прошедшей датой!");
КонецЕсли;
КонецПроцедуры
&НаСервереБезКонтекста
Функция ПолучитьАктуальнуюДатуСервера()
Возврат ТекущаяДатаСеанса();
КонецФункции
Обратите внимание, что на сервере мы используем функцию ТекущаяДатаСеанса() вместо обычного метода ТекущаяДата(). Это критически важно, если ваши пользователи работают в веб-клиенте или физически находятся в разных часовых поясах. Метод ТекущаяДатаСеанса() автоматически учитывает часовой пояс конкретного пользователя, настроенный в его параметрах сеанса, в то время как ТекущаяДата() на сервере вернет время серверной стойки, которая может находиться в другом регионе.
Контроль на уровне формы (Вариант 2) удобен для пользователя, так как сообщает об ошибке сразу при вводе. Однако он не дает 100% гарантии защиты базы данных. Документ может быть записан в обход формы: через внешнюю обработку, при обмене данными из другой конфигурации, через механизмы интеграции HTTP/REST-сервисов или при групповом изменении реквизитов.
Для обеспечения гарантированной защиты базы данных от появления документов с некорректными датами проверку необходимо дублировать (или выполнять исключительно) в Модуле объекта в предопределенной процедуре ПередЗаписью.
Проанализируем пример надежного серверного кода для модуля объекта:
Процедура ПередЗаписью(Отказ, РежимЗаписи, РежимПроведения)
// Проверяем признак загрузки данных.
// Если происходит обмен, проверку пропускаем, чтобы не нарушать синхронизацию баз.
Если ОбменДанными.Загрузка Тогда
Возврат;
КонецЕсли;
// Сравниваем только даты, без учета времени (секунд).
// Это важно, так как пользователь может создавать документ несколько минут,
// и точное время начала создания будет меньше текущего времени сервера на момент записи.
Если НачалоДня(ЭтотОбъект.Дата) < НачалоДня(ТекущаяДатаСеанса()) Тогда
Отказ = Истина;
ВызватьИсключение "Запись документа прошедшей датой заблокирована системой!";
КонецЕсли;
КонецПроцедуры
Разберем важные нюансы этого кода:
ОбменДанными.Загрузка: Если ваша база данных участвует в обмене (например, УТ -> БП), то при переносе исторических документов из торговой базы бухгалтерская база заблокирует их запись, если вы не добавите это исключение. Проверка загрузки обязательна для стабильной интеграции.НачалоДня(): Если сравнивать даты «как есть» с точностью до секунды, то даже текущий документ, который пользователь начал заполнять в 12:00, а попытался записать в 12:05, может быть заблокирован системой, поскольку время реквизита Дата (12:00) технически меньше текущего времени записи (12:05). Использование НачалоДня() нивелирует разницу внутри одних суток.Для документов оперативного учета (например, «Расходный кассовый ордер», «Реализация товаров и услуг») в платформе 1С заложен встроенный контроль проведения по времени. В свойствах таких документов в Конфигураторе устанавливается режим Оперативное проведение: Разрешить.
При попытке провести документ текущим днем в оперативном режиме система автоматически присваивает документу актуальное время сервера на момент проведения. Если пользователь пытается провести документ «задним числом», система переводит проведение в неоперативный режим или полностью выдает предупреждение в зависимости от настроек прав доступа. Это позволяет жестко регламентировать порядок ввода документов день в день.
Если вам требуется реализовать сложный запрет, который невозможно обойти никакими программными путями, можно задействовать механизм RLS (Row-Level Security) — ограничение доступа на уровне записей СУБД.
В конфигураторе для определенной роли пользователя настраиваются шаблоны ограничений на чтение или изменение данных. Мы можем прописать условие на уровне запроса к базе данных:
ГДЕ Дата >= &ТекущаяДата
Плюс этого подхода заключается в том, что проверка выполняется непосредственно на сервере СУБД (MS SQL, PostgreSQL). Ни один скрипт, ни одна внешняя обработка, запущенная под пользователем с данным ограничением, не сможет физически записать объект, нарушающий данное условие. Минус — дополнительная нагрузка на производительность СУБД при массовом проведении документов.
Для достижения максимальной надежности и удобства работы пользователей рекомендуется комбинировать описанные методы:
ТекущаяДатаСеанса(), а окончательный жесткий контроль (свойство Отказ = Истина) выносите в процедуру ПередЗаписью модуля объекта с обязательной проверкой ОбменДанными.Загрузка.