Разберем распространенную проблему, с которой сталкиваются начинающие разработчики при программном заполнении документов. Представим ситуацию: вы программно создаете или модифицируете документ (например, РеализацияТоваровУслуг) и пытаетесь связать его с другим документом (например, СчетНаОплатуПокупателю), зная только номер последнего. Вы пытаетесь найти документ-основание по номеру, присваиваете полученную ссылку реквизиту, но при записи или проведении обнаруживаете, что реквизит остался пустым. При этом остальные реквизиты заполняются корректно, а документ проводится без ошибок.
Давайте проанализируем, почему так происходит, выясним причину и рассмотрим несколько вариантов решения этой задачи — от простого исправления кода до правильного архитектурного проектирования.
Для поиска документа по его номеру в платформе 1С предусмотрен стандартный метод менеджера документа НайтиПоНомеру(). Часто разработчики вызывают его следующим образом:
СсылкаНаСчет = Документы.СчетНаОплатуПокупателю.НайтиПоНомеру(НомерСчета);
На первый взгляд, код выглядит абсолютно логично. Однако на практике переменная СсылкаНаСчет очень часто возвращает пустую ссылку (Документы.СчетНаОплатуПокупателю.ПустаяСсылка()). Почему так происходит?
Дело в том, что для большинства документов в конфигурациях 1С настроена периодическая нумерация (в пределах года, квартала или месяца). Это означает, что уникальность номеров гарантируется только в рамках заданного периода. Например, документ с номером "00000001" может законно существовать и в 2023 году, и в 2024 году.
Если мы обратимся к синтакс-помощнику, то увидим, что метод имеет следующий синтаксис:
НайтиПоНомеру(<Номер>, <ДатаИнтервала>)
Второй параметр ДатаИнтервала является ключевым для документов с периодической нумерацией:
01.01.0001).Если вы хотите продолжить использовать метод НайтиПоНомеру(), вам обязательно нужно передать дату для определения интервала поиска. Рассмотрим пример, где мы передаем дату создаваемого документа реализации для поиска счета в том же временном периоде:
// Предположим, у нас есть ДатаДокумента и НомерСчета
ДатаПоиска = Объект.Дата;
СсылкаНаСчет = Документы.СчетНаОплатуПокупателю.НайтиПоНомеру(НомерСчета, ДатаПоиска);
Если Не СсылкаНаСчет.Пустая() Тогда
Объект.СчетНаОплату = СсылкаНаСчет;
Иначе
Сообщить("Документ Счет на оплату с номером " + НомерСчета + " не найден в периоде даты " + ДатаПоиска);
КонецЕсли;
Этот способ решит проблему пустой ссылки, но он имеет ряд существенных недостатков, которые могут привести к ошибкам в реальной многопользовательской базе данных.
Использование метода НайтиПоНомеру() в реальной разработке настоятельно не рекомендуется по следующим причинам:
НайтиПоНомеру() вернет первый попавшийся документ, что приведет к грубым ошибкам в учете."00БП-000015"). Если пользователь ввел на форме или передал из внешней интеграции число 15 или строку "15", обычный поиск не сопоставит его с реальным номером. Придется программно приводить номер к стандартному формату конфигурации, что усложняет код.Гораздо более надежным, быстрым и гибким способом является использование объекта Запрос. Запрос позволяет искать документы с учетом дополнительных аналитических разрезов (например, по конкретной организации, контрагенту или договору), а также обходить проблемы с префиксами с помощью оператора ПОДОБНО.
Проанализируем пример построения такого запроса:
// Создаем запрос для поиска счета на оплату
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ ПЕРВЫЕ 1
| Счет.Ссылка КАК Ссылка
|ИЗ
| Документ.СчетНаОплатуПокупателю КАК Счет
|ГДЕ
| Счет.Номер ПОДОБНО &Номер
| И Счет.Организация = &Организация
| И Счет.Контрагент = &Контрагент
| И Счет.Проведен = ИСТИНА
|УПОРЯДОЧИТЬ ПО
| Счет.Дата УБЫВ";
// Передаем параметры.
// Используем "%" для поиска номера, даже если он передан без лидирующих нулей
Запрос.УстановитьПараметр("Номер", "%" + СокрЛП(НомерСчета));
Запрос.УстановитьПараметр("Организация", Объект.Организация);
Запрос.УстановитьПараметр("Контрагент", Объект.Контрагент);
Выборка = Запрос.Выполнить().Выбрать();
Если Выборка.Следующий() Тогда
Объект.СчетНаОплату = Выборка.Ссылка;
Иначе
Сообщить("Не удалось однозначно определить Счет на оплату по указанным параметрам!");
КонецЕсли;
Преимущества данного подхода:
ПОДОБНО и знака процента позволяет находить документ, даже если в базе номер записан как "0000-000125", а пользователь передал просто строку "125".УПОРЯДОЧИТЬ ПО Счет.Дата УБЫВ гарантирует, что если в системе все же окажется несколько подходящих документов, мы выберем самый актуальный (последний по дате).Если бизнес-логика вашей задачи подразумевает создание документа "Реализация товаров и услуг" для закрытия конкретного "Счета на оплату", то передача текстового номера между документами является архитектурной ошибкой. Правильный подход — оперировать ссылочными типами данных на уровне интерфейса и бизнес-логики.
В платформе 1С для этого реализован стандартный механизм заполнения "На основании". Давайте посмотрим, как это сделать программно:
// 1. Получаем ссылку на исходный счет на оплату
СсылкаНаСчет = ВыбранныйСчетНаОплату;
// 2. Создаем новый документ реализации
НоваяРеализация = Документы.РеализацияТоваровУслуг.СоздатьДокумент();
// 3. Устанавливаем дату и другие базовые реквизиты, если необходимо
НоваяРеализация.Дата = ТекущаяДата();
// 4. Используем стандартный метод заполнения на основании
НоваяРеализация.Заполнить(СсылкаНаСчет);
// 5. Записываем и проводим документ
НоваяРеализация.Записать(РежимЗаписиДокумента.Проведение);
Метод Заполнить() автоматически вызывает обработчик ОбработкаЗаполнения в модуле объекта документа РеализацияТоваровУслуг. При этом: