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