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