В современных конфигурациях «1С:Предприятие 8», таких как «Управление торговлей» редакции 11, «ERP Управление предприятием» или «Комплексная автоматизация», активно используется «Библиотека стандартных подсистем» (БСП). Одной из крайне полезных функций этой библиотеки является механизм персональных напоминаний. Он позволяет пользователям гибко настраивать уведомления по важным объектам системы — например, напомнить менеджеру о необходимости отгрузки или оплаты по заказу клиента.
Однако при попытке автоматизировать этот процесс и создавать напоминания программно (например, при проведении или записи документов) начинающие разработчики часто сталкиваются с тем, что записи в регистре сведений не появляются или система ведет себя непредсказуемо. В этой статье мы подробно разберем типичные ошибки при работе с регистром напоминаний, рассмотрим правильные способы программного создания записей и выясним важные архитектурные нюансы, о которых необходимо помнить.
Проанализируем ситуацию, представленную на форуме. Разработчик пытается создать запись в регистре сведений НапоминанияПользователя при записи заказа клиента. Посмотрим на фрагмент его кода, который не привел к желаемому результату, и выясним причины неудач.
Во-первых, в коде присутствует большое количество опечаток и синтаксических ошибок, из-за которых код в реальной базе даже не пройдет синтаксический контроль:
СушествуюшиеНапоминания (вместо «Существующие»), СоэдатьНаборЗаписей (вместо «СоздатьНаборЗаписей»), ТекушийПользователь (вместо «ТекущийПользователь»).СпособыУстановкиЗремениНапомикания вместо правильного системного идентификатора СпособыУстановкиВремениНапоминания.- и *) вместо оператора конкатенации строк (+).СоздатьМенеджерЗаписи{).Во-вторых, допущена серьезная логическая ошибка при работе с объектами базы данных 1С. Разработчик получает пустой набор записей (поскольку для нового заказа напоминаний еще нет), после чего в ветке Если инициализирует объект типа РегистрСведенийМенеджерЗаписи, заполняет его поля, но забывает вызвать метод Записать() для самого менеджера. Вместо этого в конце процедуры вызывается СуществующиеНапоминания.Записать(). Поскольку набор записей СуществующиеНапоминания остался абсолютно пустым, его запись просто перезаписывает данные в базе данных «пустотой», затирая возможные старые данные и игнорируя созданный менеджер записи.
В-третьих, в ветке Иначе разработчик пытается изменить поле ВремяСобытия непосредственно внутри строки прочитанного набора записей: Напоминание.ВремяСобытия = .... Однако важно помнить, что поле ВремяСобытия является измерением (то есть ключевым полем) регистра сведений НапоминанияПользователя. Напрямую изменять ключевые поля в строках существующего набора записей без очистки старого ключа и создания нового нельзя — это приведет к ошибкам записи или непредсказуемому поведению СУБД.
В-четвертых, не были заполнены обязательные служебные реквизиты. Для того чтобы механизм напоминаний БСП успешно вывел окно уведомления на экран пользователя, обязательно должны быть заполнены ресурс СрокНапоминания и реквизит Описание. Без них система просто проигнорирует запись при анализе актуальных уведомлений.
Теперь, когда причины ошибок понятны, разберем по шагам, как решить эту задачу правильно.
Самый надежный и правильный подход при работе с любыми стандартными подсистемами 1С — это использование готового программного интерфейса (API), предоставляемого разработчиками БСП. В конфигурации «Управление торговлей 11» для этого предусмотрен общий модуль НапоминанияПользователя.
Использование этого модуля избавляет нас от необходимости вручную контролировать структуру регистра, рассчитывать ключевые поля и беспокоиться об изменениях метаданных при будущих обновлениях системы. Метод УстановитьНапоминание() автоматически проверит наличие существующих записей для данного документа, обновит их или создаст новые.
Рассмотрим пример правильного кода с использованием БСП:
Если ЗначениеЗаполнено(Объект.ДатаОтгрузки) Тогда
// Рассчитываем точное время срабатывания (например, за 3 дня до даты отгрузки)
ВремяНапоминания = Объект.ДатаОтгрузки - (60 * 60 * 24 * 3);
// Формируем понятный и информативный текст для пользователя
ТекстНапоминания = СтрШаблон("Проверить состояние заказа клиента № %1 от %2",
Объект.Номер,
Формат(Объект.Дата, "ДЛФ=D"));
// Используем стандартный метод БСП для создания или обновления напоминания
НапоминанияПользователя.УстановитьНапоминание(
ТекстНапоминания, // Текст, который увидит пользователь
ВремяНапоминания, // Время, когда должно сработать уведомление
0, // Смещение относительно события в секундах (0 - точно в указанное время)
Объект.Ссылка // Предмет напоминания (ссылка на наш документ)
);
КонецЕсли;
Этот простой и лаконичный код берет на себя всю рутину по работе с базой данных, гарантируя корректное отображение всплывающего окна у пользователя в назначенное время.
Если перед вами стоит специфическая задача, требующая тонкой ручной настройки записи (например, создание напоминаний для другого пользователя или заполнение специфических служебных реквизитов), можно выполнить запись в регистр сведений напрямую. Но делать это нужно строго с соблюдением правил работы с объектом РегистрСведенийМенеджерЗаписи.
Проанализируем правильный шаблон кода для прямой работы с регистром:
Если ЗначениеЗаполнено(Объект.ДатаОтгрузки) Тогда
ВремяСобытия = Объект.ДатаОтгрузки - (60 * 60 * 24 * 3);
ТекущийПользователь = Пользователи.ТекущийПользователь(); // Получаем авторизованного пользователя
// Инициализируем менеджер записи регистра
МЗ = РегистрыСведений.НапоминанияПользователя.СоздатьМенеджерЗаписи();
// Указываем ключевые поля (измерения), по которым система будет искать запись в базе
МЗ.Пользователь = ТекущийПользователь;
МЗ.Источник = Объект.Ссылка;
МЗ.ВремяСобытия = ВремяСобытия;
// Пытаемся прочитать существующую запись, чтобы не создавать дубли
МЗ.Прочитать();
// Заполняем ресурсы и реквизиты (новые либо перезаписываем старые данные)
МЗ.СрокНапоминания = ВремяСобытия; // Критически важный ресурс для работы подсистемы!
МЗ.СпособУстановкиВремениНапоминания = Перечисления.СпособыУстановкиВремениНапоминания.ВУказанноеВремя;
МЗ.Описание = СтрШаблон("Срочно отгрузить заказ клиента № %1 от %2", Объект.Номер, Формат(Объект.Дата, "ДЛФ=D"));
МЗ.ПредставлениеИсточника = Строка(Объект.Ссылка);
// Записываем конкретно этот подготовленный менеджер записи
МЗ.Записать();
КонецЕсли;
В данном случае мы четко разделили логику: сначала заполнили ключевые поля, попытались выполнить метод Прочитать() для получения уже существующей строки, дозаполнили необходимые ресурсы и принудительно вызвали Записать() именно у объекта менеджера записи.
При реализации автоматического создания напоминаний разработчики часто упускают из виду важные системные ограничения платформы 1С и архитектуру конфигурации. Разберем две наиболее важные проблемы: ограничение прав доступа и правильный выбор места для встраивания кода.
В БСП на регистр сведений НапоминанияПользователя наложены жесткие ограничения прав доступа на уровне записей (RLS). По умолчанию обычный пользователь имеет право просматривать, изменять и создавать записи в этом регистре исключительно для самого себя. Попытка создать запись, где поле Пользователь заполнено другим сотрудником, приведет к ошибке нарушения прав доступа.
Если по вашей бизнес-логике требуется, чтобы система автоматически создавала напоминание для менеджера заказа (который может не совпадать с текущим пользователем, проводящим документ), данный код обязательно нужно выполнять в привилегированном режиме. Для этого логику записи следует вынести в общий модуль с установленным флагом «Привилегированный» или использовать конструкцию:
УстановитьПривилегированныйРежим(Истина);
// Код создания напоминания для другого пользователя
УстановитьПривилегированныйРежим(Ложь);
В исходном сообщении автор пытался написать код в событии формы документа ПослеЗаписиНаСервере. Это крайне не рекомендуется делать по следующим причинам:
Правильное решение: перенести логику создания напоминания в Модуль объекта документа ЗаказКлиента в предопределенный обработчик события ПриЗаписи. При этом крайне важно предусмотреть проверку режима загрузки данных, чтобы избежать зацикливаний и создания мусорных записей при обменах:
Процедура ПриЗаписи(Отказ)
// Если идет автоматический обмен данными, напоминания создавать не нужно
Если ОбменДанными.Загрузка Тогда
Возврат;
КонецЕсли;
// Вызываем логику создания напоминания
СоздатьАвтоматическоеНапоминание();
КонецПроцедуры
Соблюдение этих несложных правил позволит вам создавать чистый, отказоустойчивый код, который будет стабильно работать при любых сценариях использования информационной базы.