В процессе разработки конфигураций на платформе 1С:Предприятие 8 часто возникает бизнес-задача: ограничить количество документов определенного типа одним экземпляром в день. Например, это может быть «Закрытие смены», «Отчет о розничных продажах» или специфический регламентный документ. Мы разберем несколько способов реализации этой логики, проанализируем их эффективность и научимся избегать типичных ошибок, таких как «состояние гонки» при одновременной работе пользователей.
Это самый простой и надежный с точки зрения платформенных механизмов способ. Идея заключается в том, чтобы создать вспомогательный РегистрСведений, который будет автоматически контролировать уникальность записи за период.
Рассмотрим реализацию по шагам:
РегистрСведений (например, с именем КонтрольДокументовЗаДень).День.Подчинение регистратору.Организация или Подразделение).Как это работает: При проведении документа система пытается записать данные в этот регистр. Поскольку периодичность установлена в «День», платформа 1С на уровне СУБД не позволит создать вторую запись с тем же набором измерений за ту же дату. Если пользователь попытается провести второй документ, система выдаст стандартную ошибку о неуникальности записей в регистре.
Преимущества: Минимум кода, высокая надежность, автоматическая очистка записей при отмене проведения документа.
Недостатки: Сообщение об ошибке выглядит техническим («Запись с такими ключевыми полями уже существует»), что может сбивать с толку обычного пользователя. Также это занимает дополнительное место в базе данных.
Если нам нужно более гибкое решение и понятные пользователю сообщения, проанализируем вариант с написанием кода в модуле объекта. Мы будем использовать обработчик события ПередЗаписью или ОбработкаПроверкиЗаполнения. Однако наиболее правильно выполнять проверку именно при проведении, чтобы не мешать пользователю сохранять черновики.
Разберем пример кода, который нужно разместить в модуле документа:
Процедура ПередЗаписью(Отказ, РежимЗаписи, РежимПроведения)
Если ОбменДанными.Загрузка Тогда
Возврат;
КонецЕсли;
Если РежимЗаписи = РежимЗаписиДокумента.Проведение Тогда
// 1. Установка управляемой блокировки (Критически важно!)
Блокировка = Новый БлокировкаДанных;
ЭлементБлокировки = Блокировка.Добавить("Документ.НашТипДокумента");
ЭлементБлокировки.УстановитьПараметр("Дата", Новый Диапазон(НачалоДня(Дата), КонецДня(Дата)));
Блокировка.Заблокировать();
// 2. Проверка наличия другого проведенного документа
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ ПЕРВЫЕ 1
| НашДокумент.Ссылка КАК Ссылка
|ИЗ
| Документ.НашТипДокумента КАК НашДокумент
|ГДЕ
| НашДокумент.Дата МЕЖДУ &НачалоПериода И &КонецПериода
| И НашДокумент.Проведен = ИСТИНА
| И НашДокумент.Ссылка <> &ТекущаяСсылка";
Запрос.УстановитьПараметр("НачалоПериода", НачалоДня(Дата));
Запрос.УстановитьПараметр("КонецПериода", КонецДня(Дата));
Запрос.УстановитьПараметр("ТекущаяСсылка", Ссылка);
РезультатЗапроса = Запрос.Выполнить();
Если Не РезультатЗапроса.Пустой() Тогда
Выборка = РезультатЗапроса.Выбрать();
Выборка.Следующий();
ТекстСообщения = СтрШаблон("За этот день уже проведен документ данного типа! (Ссылка: %1)", Выборка.Ссылка);
ОбщегоНазначенияКлиентСервер.СообщитьПользователю(ТекстСообщения, , , , Отказ);
Возврат;
КонецЕсли;
КонецЕсли;
КонецПроцедуры
Проанализируем важные детали этого кода:
ОбменДанными.Загрузка: Мы пропускаем проверку, если данные загружаются программно (например, через РИБ или из другой системы), чтобы не нарушить целостность обмена.БлокировкаДанных. Это необходимо для исключения «состояния гонки». Без этого два пользователя могут одновременно нажать кнопку «Провести», оба запроса не найдут существующий документ в базе (так как транзакции еще не завершены), и в итоге в базе окажется два документа на одну дату.Ссылка <> &ТекущаяСсылка, иначе при перепроведении уже существующего документа система найдет саму себя и выдаст ошибку.ВЫБРАТЬ ПЕРВЫЕ 1: Оптимизирует запрос — нам не нужно знать количество документов, достаточно факта наличия хотя бы одного.Если аналогичную проверку нужно внедрить для нескольких типов документов, или если вы работаете с типовой конфигурацией и не хотите ее изменять (снимать с поддержки), используем Подписку на события.
Разберем алгоритм:
ПодпискаНаСобытие в дереве метаданных.ПередЗаписью.Источник и выполнять запрос к соответствующей таблице метаданных.Этот метод считается наиболее «элегантным» в современной разработке, так как он позволяет централизованно управлять логикой контроля для всей системы.
Просто выдать ошибку — это только половина дела. Хорошим тоном считается помочь пользователю найти тот документ, который мешает проведению. Выясним, как это сделать лучше:
Вместо обычного метода Сообщить(), используйте формирование сообщения с навигационной ссылкой. В современных управляемых формах 1С это позволяет пользователю прямо из окна сообщения кликнуть на ссылку и мгновенно открыть документ-дубликат.
Также рассмотрим возможность использования Функциональных опций. Вы можете вынести этот контроль в настройки программы. Например, создать константу ЗапретитьДублиДокументовВДень. В коде проверки добавим условие: если константа ложна, проверка пропускается. Это позволит администратору системы гибко управлять правилами без помощи программиста.
Если ваша информационная база работает в нескольких часовых поясах, определение «начала дня» может вызвать сложности. В таком случае рекомендуется использовать Универсальное время (UTC) или приводить дату документа к времени сеанса сервера. Если для бизнеса «день» — это календарные сутки по московскому времени, используйте функции работы с датами с явным указанием правил смещения времени, чтобы избежать ситуации, когда один документ записан «вчера» по серверу, но «сегодня» по клиенту.
Подведем итог: Для жесткого и простого контроля используйте периодический РегистрСведений. Для создания гибкой системы с дружелюбным интерфейсом и расширенной логикой выбирайте программную проверку с обязательным использованием управляемых блокировок и учетом режима загрузки данных.