Как при создании на основании открыть существующий документ или переиспользовать помеченный на удаление?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При разработке бизнес-логики в 1С часто возникает задача модифицировать стандартный механизм ввода на основании. Например, если подчиненный документ уже был создан ранее или требуется восстановить помеченный на удаление документ вместо создания дубликата. Разберем особенности платформы 1С:Предприятие 8.3 при работе с управляемыми формами, ограничения стандартного механизма заполнения и несколько способов реализации такой логики.

Архитектурные ограничения механизма ОбработкаЗаполнения

Проанализируем, почему нельзя просто «подменить» ссылку или объект внутри стандартной процедуры ОбработкаЗаполнения модуля объекта:

  1. Процедура ОбработкаЗаполнения вызывается платформой в контексте нового, еще не записанного объекта базы данных (ЭтоНовый() = Истина).
  2. Платформа не позволяет изменить существующую ссылку (Ссылка) на произвольный GUID из базы данных у уже инициализированного в памяти объекта.
  3. Свойство ДополнительныеСвойства объекта ДокументОбъект не переносится автоматически в реквизит управляемой формы Объект (имеющий тип ДанныеФормыСтруктура). Поэтому передать флаги или найденную ссылку из модуля объекта на форму через ДополнительныеСвойства без дополнительной сериализации не удастся.

Исходя из этого, задачу разделяют на два архитектурных сценария: переопределение клиентской команды создания и повторное использование номеров документов.

Способ 1. Переопределение команды ввода на основании на клиенте

Наиболее надежный и рекомендуемый способ — перехватить ввод на этапе нажатия кнопки пользователем. Вместо стандартного механизма платформы мы создаем собственную команду или переопределяем подключаемую команду (БСП).

Разберем по шагам логику клиент-серверного взаимодействия:

  1. На клиенте в форме документа-основания вызывается серверная функция поиска подходящего документа (например, помеченного на удаление или ранее созданного по этому основанию).
  2. Если документ найден, открываем форму существующего объекта. При необходимости снимаем пометку удаления и перезаполняем его.
  3. Если документ не найден, открываем форму нового документа стандартным образом с передачей параметров заполнения.

Посмотрим на пример реализации в модуле формы исходного документа:


&НаКлиенте
Процедура СоздатьПодчиненныйДокумент(Команда)
    
    НайденнаяСсылка = НайтиСуществующийДокументНаСервере(Объект.Ссылка);
    
    Если ЗначениеЗаполнено(НайденнаяСсылка) Тогда
        // Открываем существующий документ
        ПараметрыОткрытия = Новый Структура("Ключ", НайденнаяСсылка);
        ОткрытьФорму("Документ.РеализацияТоваровУслуг.ФормаОбъекта", ПараметрыОткрытия);
    Иначе
        // Открываем форму нового документа с заполнением
        ПараметрыЗаполнения = Новый Структура("Основание", Объект.Ссылка);
        ПараметрыОткрытия = Новый Структура("ЗначенияЗаполнения", ПараметрыЗаполнения);
        ОткрытьФорму("Документ.РеализацияТоваровУслуг.Форма.ФормаДокумента", ПараметрыОткрытия);
    КонецЕсли;

КонецПроцедуры

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

КонецФункции

Способ 2. Перенаправление из формы создаваемого документа

Если изменить кнопку в источнике невозможно и необходимо перехватить ввод непосредственно в форме создаваемого документа, логику можно поместить в обработчик ПриСозданииНаСервере или ПриОткрытии целевого документа.

Рассмотрим пример проверки основания в модуле формы подчиненного документа:


&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)

    // Проверяем, передан ли параметр основания при интерактивном создании
    Если Параметры.Свойство("ЗначенияЗаполнения") И Параметры.ЗначенияЗаполнения.Свойство("ДокументОснование") Тогда
        
        Основание = Параметры.ЗначенияЗаполнения.ДокументОснование;
        СуществующаяСсылка = ПолучитьДокументПоОснованию(Основание);
        
        Если ЗначениеЗаполнено(СуществующаяСсылка) Тогда
            // Сохраняем найденную ссылку во временный реквизит формы для открытия на клиенте
            Параметры.Свойство("Ключ", СуществующаяСсылка);
            ЭтаФорма.НайденныйДокумент = СуществующаяСсылка;
        КонецЕсли;
        
    КонецЕсли;

КонецПроцедуры

&НаКлиенте
Процедура ПриОткрытии(Отказ)

    Если ЗначениеЗаполнено(НайденныйДокумент) Тогда
        Отказ = Истина; // Отменяем открытие текущей формы нового объекта
        ПараметрыОткрытия = Новый Структура("Ключ", НайденныйДокумент);
        ОткрытьФорму("Документ.РеализацияТоваровУслуг.ФормаОбъекта", ПараметрыОткрытия);
    КонецЕсли;

КонецПроцедуры

Способ 3. Переиспользование номеров удаленных документов

Выясним причину, по которой часто требуется «подмена» помеченного документа. Во многих случаях разработчику нужен не сам объект, а его освободившийся номер для устранения пропусков в нумерации. В 1С стандартная настройка «Режим автонумерации объектов: Освобождать автоматически» работает только при отмене создания объекта в рамках сеанса и не заполняет пропуски задним числом.

Для переиспользования номера помеченного на удаление документа используем событие модуля объекта ПриУстановкеНовогоНомера или подписку на события:


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

КонецПроцедуры

Важно: При выполнении операций перезаписи старых объектов и назначения одинаковых номеров необходимо контролировать транзакционность и устанавливать управляемые блокировки данных (БлокировкаДанных), чтобы исключить конфликты блокировок при одновременной работе нескольких пользователей.

← На главную