Как предотвратить создание дублирующих документов «Продажа» на основании «Записи» и открывать уже существующий в 1С?

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

При работе в системе 1С часто возникает ситуация, когда на основании одного документа требуется создать другой. Например, как в нашем случае, из документа «Запись» формируется документ «Продажа». Однако, при повторном нажатии кнопки формирования, система по умолчанию создает новый экземпляр документа «Продажа», что приводит к появлению множества дубликатов. Это не только засоряет базу данных, но и усложняет учет и анализ информации.

Мы с вами разберем, как реализовать логику, при которой система сначала проверяет наличие уже созданного документа «Продажа» на основании текущей «Записи». Если такой документ существует, мы будем открывать его для редактирования, а не создавать новый. Если же связанного документа еще нет, тогда мы его сформируем и откроем.

Определяем основной подход: реквизит «ДокументОснование» и запрос

Для эффективного решения этой задачи мы будем использовать наиболее распространенный и рекомендуемый способ в 1С: добавим в документ «Продажа» специальный реквизит, который будет хранить ссылку на документ-основание – в нашем случае это будет ссылка на документ «Запись». Затем, перед формированием нового документа, мы выполним запрос к базе данных, чтобы проверить наличие связанного документа.

Шаг 1: Добавляем реквизит «ДокументОснование» в документ «Продажа»

Первым делом нам необходимо установить связь между документами «Запись» и «Продажа». Для этого мы добавим новый реквизит в объект метаданных «Документ.Продажа». Этот реквизит будет хранить уникальную ссылку на документ «Запись», на основании которого была сформирована конкретная «Продажа».

  1. Откройте конфигуратор 1С.
  2. Перейдите в раздел «Документы» и найдите ваш документ «Продажа».
  3. Раскройте его свойства и перейдите на вкладку «Реквизиты».
  4. Создайте новый реквизит, например, с именем ДокументОснование.
  5. Установите тип данных для этого реквизита как ДокументСсылка.Запись. Это гарантирует, что в данном реквизите могут храниться только ссылки на документы типа «Запись».

После добавления этого реквизита в конфигурации, каждый раз при создании нового документа «Продажа» на основании «Записи», мы будем обязаны заполнять этот реквизит ссылкой на исходный документ «Запись». Это ключевой момент, который позволит нам в дальнейшем находить уже созданные «Продажи».

Шаг 2: Реализуем проверку наличия существующего документа «Продажа»

Теперь, когда у нас есть механизм для связывания документов, мы можем приступить к написанию кода. Этот код будет выполняться при нажатии на кнопку «ОформитьПродажу» на форме документа «Запись». Наша логика будет состоять из двух основных этапов:

  1. Поиск существующего документа «Продажа» по реквизиту «ДокументОснование».
  2. Принятие решения: открыть найденный документ или создать новый.

Рассмотрим подробнее реализацию этой логики с использованием запроса.

Функция для поиска существующей «Продажи» на сервере

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

Добавим следующую функцию в модуль формы документа «Запись» (или в общий модуль, если такая логика будет использоваться в нескольких местах), помечая ее как &НаСервере:


&НаСервере
Функция НайтиСуществующуюПродажу(СсылкаНаЗапись)

    Запрос = Новый Запрос;
    Запрос.Текст =
        "ВЫБРАТЬ
        |    Продажа.Ссылка
        |ИЗ
        |    Документ.Продажа КАК Продажа
        |ГДЕ
        |    Продажа.ДокументОснование = &ДокументОснование";

    Запрос.УстановитьПараметр("ДокументОснование", СсылкаНаЗапись);

    РезультатЗапроса = Запрос.Выполнить();

    Если Не РезультатЗапроса.Пустой() Тогда
        Выборка = РезультатЗапроса.Выбрать();
        Выборка.Следующий();
        Возврат Выборка.Ссылка;
    Иначе
        Возврат Неопределено;
    КонецЕсли;

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

Давайте проанализируем этот код:

Процедура обработки кнопки «ОформитьПродажу» на клиенте

Теперь мы реализуем процедуру, которая будет вызываться при нажатии на кнопку «ОформитьПродажу» на форме документа «Запись». Эта процедура будет выполняться на клиенте, но она будет вызывать нашу серверную функцию для получения информации из базы данных.

Добавим следующую процедуру в модуль формы документа «Запись», помечая ее как &НаКлиенте:


&НаКлиенте
Процедура ОформитьПродажу(Команда)

    // Получаем ссылку на текущий документ "Запись"
    СсылкаНаЗапись = Объект.Ссылка;

    // Ищем существующую "Продажу"
    СуществующаяПродажа = НайтиСуществующуюПродажу(СсылкаНаЗапись);

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

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

Разберем эту клиентскую процедуру по шагам:

Альтернативные методы и важные нюансы

Метод НайтиПоРеквизиту()

Для поиска документа по значению одного реквизита существует более простой метод менеджера документов – НайтиПоРеквизиту(). Этот метод может быть полезен в ситуациях, когда вам нужно найти документ только по одному уникальному реквизиту.

Пример использования НайтиПоРеквизиту():


&НаСервере
Функция НайтиСуществующуюПродажуПоРеквизиту(СсылкаНаЗапись)
    СуществующаяПродажаСсылка = Документы.Продажа.НайтиПоРеквизиту("ДокументОснование", СсылкаНаЗапись);
    Возврат СуществующаяПродажаСсылка;
КонецФункции

Важные моменты при использовании НайтиПоРеквизиту():

Различные способы открытия формы документа по ссылке

Мы уже рассмотрели основной способ открытия формы с помощью ОткрытьФорму() и параметра Ключ. Однако существуют и другие методы:

  1. ОткрытьФорму() с параметром Ключ:

    Этот метод является наиболее универсальным и рекомендуемым для открытия формы конкретного объекта (документа, элемента справочника) по его ссылке.

    
    ОткрытьФорму("Документ.Продажа.ФормаДокумента", Новый Структура("Ключ", СуществующаяПродажа));
    
  2. ПоказатьЗначение():

    Функция ПоказатьЗначение() открывает стандартную форму представления для любого объекта 1С, на который указывает переданное значение (обычно это ссылка). Она автоматически определяет тип объекта и открывает соответствующую форму.

    ПоказатьЗначение(СуществующаяПродажа);

    Этот метод удобен своей простотой, если вам не нужно указывать конкретную форму или передавать дополнительные параметры, помимо самой ссылки.

  3. Метод Открыть() объекта документа:

    Вы можете получить объект документа по ссылке и затем вызвать его метод Открыть(). Этот метод также откроет форму документа.

    
    ОбъектПродажи = СуществующаяПродажа.ПолучитьОбъект();
    Если ОбъектПродажи <> Неопределено Тогда
        ОбъектПродажи.Открыть();
    КонецЕсли;
    

    Однако, для этого сначала нужно получить сам объект из ссылки (ПолучитьОбъект()), что может быть избыточно, если вам нужна только форма, а не сам объект для модификации.

Важность проверки существования ссылки

Перед тем как пытаться открыть форму документа по ссылке, критически важно убедиться, что эта ссылка заполнена и не является пустой или Неопределено. Для этого мы использовали функцию ЗначениеЗаполнено(). Если попытаться открыть форму по пустой ссылке, это может привести к ошибке выполнения или открытию пустой формы нового документа вместо ожидаемого.


Если ЗначениеЗаполнено(СуществующаяПродажа) Тогда
    // Открываем форму
Иначе
    // Создаем новый документ
КонецЕсли;

Различие между реквизитами формы и реквизитами документа

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

Для нашей задачи по связыванию документов и поиску в базе данных мы обязательно должны использовать реквизит непосредственно в объекте метаданных «Документ.Продажа».

Обработка дублей и типовые механизмы

В более широком контексте предотвращения дублирования данных (не только документов, но и элементов справочников) в 1С существуют типовые механизмы:

Однако, для сценария «документ на основании другого документа» описанный выше программный подход (с реквизитом-основанием и запросом) является наиболее точным и подходящим, так как он позволяет контролировать уникальность связи между конкретными документами.

Резюме для пользователя: пошаговое решение

Итак, чтобы решить проблему создания дублирующихся документов «Продажа» на основании «Записи» и вместо этого открывать уже существующий документ, мы должны выполнить следующие действия:

  1. Добавьте реквизит ДокументОснование с типом ДокументСсылка.Запись к документу «Продажа» в конфигурации. Этот реквизит будет хранить ссылку на исходный документ «Запись».

  2. В процедуре обработки кнопки ОформитьПродажу на форме документа «Запись» реализуйте логику:

    1. Получите ссылку на текущий документ «Запись» (например, Объект.Ссылка).

    2. Выполните запрос (или используйте Документы.Продажа.НайтиПоРеквизиту()) к базе данных, чтобы найти документ «Продажа» по значению реквизита ДокументОснование, который должен быть равен ссылке на текущую «Запись».

    3. Проверьте результат запроса с помощью функции ЗначениеЗаполнено():

      • Если документ «Продажа» найден, получите его ссылку и откройте форму этого документа, используя метод ОткрытьФорму() с параметром Ключ:
        
        ОткрытьФорму("Документ.Продажа.ФормаДокумента", Новый Структура("Ключ", СуществующаяПродажаСсылка));
        
      • Если документ не найден, создайте новый документ «Продажа» (Документы.Продажа.СоздатьДокумент()), обязательно заполните его реквизит ДокументОснование ссылкой на текущую «Запись», заполните другие необходимые реквизиты, запишите документ (НоваяПродажа.Записать()) и затем откройте форму нового документа по его ссылке.

Таким образом, мы с вами успешно реализовали механизм, который не только предотвращает создание ненужных дубликатов, но и значительно улучшает пользовательский опыт, позволяя быстро вернуться к редактированию уже существующего связанного документа.

← На главную