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