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