Почему не заполняется реквизит документа при программном поиске по номеру в 1С?

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

Разберем распространенную проблему, с которой сталкиваются начинающие разработчики при программном заполнении документов. Представим ситуацию: вы программно создаете или модифицируете документ (например, РеализацияТоваровУслуг) и пытаетесь связать его с другим документом (например, СчетНаОплатуПокупателю), зная только номер последнего. Вы пытаетесь найти документ-основание по номеру, присваиваете полученную ссылку реквизиту, но при записи или проведении обнаруживаете, что реквизит остался пустым. При этом остальные реквизиты заполняются корректно, а документ проводится без ошибок.

Давайте проанализируем, почему так происходит, выясним причину и рассмотрим несколько вариантов решения этой задачи — от простого исправления кода до правильного архитектурного проектирования.

Выясняем причину: особенности работы метода НайтиПоНомеру()

Для поиска документа по его номеру в платформе 1С предусмотрен стандартный метод менеджера документа НайтиПоНомеру(). Часто разработчики вызывают его следующим образом:


СсылкаНаСчет = Документы.СчетНаОплатуПокупателю.НайтиПоНомеру(НомерСчета);

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

Дело в том, что для большинства документов в конфигурациях 1С настроена периодическая нумерация (в пределах года, квартала или месяца). Это означает, что уникальность номеров гарантируется только в рамках заданного периода. Например, документ с номером "00000001" может законно существовать и в 2023 году, и в 2024 году.

Если мы обратимся к синтакс-помощнику, то увидим, что метод имеет следующий синтаксис:

НайтиПоНомеру(<Номер>, <ДатаИнтервала>)

Второй параметр ДатаИнтервала является ключевым для документов с периодической нумерацией:

Решение 1. Исправление поиска через НайтиПоНомеру()

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


// Предположим, у нас есть ДатаДокумента и НомерСчета
ДатаПоиска = Объект.Дата; 
СсылкаНаСчет = Документы.СчетНаОплатуПокупателю.НайтиПоНомеру(НомерСчета, ДатаПоиска);

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

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

Почему поиск по номеру через методы менеджера — это плохая практика

Использование метода НайтиПоНомеру() в реальной разработке настоятельно не рекомендуется по следующим причинам:

  1. Проблема неуникальности номеров в одном периоде. В информационных базах часто ведется учет по нескольким организациям или используются распределенные информационные базы (РИБ). В таких случаях документы могут иметь одинаковые номера, но отличаться префиксом или принадлежать разным организациям. Метод НайтиПоНомеру() вернет первый попавшийся документ, что приведет к грубым ошибкам в учете.
  2. Проблема префиксов и лидирующих нулей. Номера документов в 1С хранятся в виде текстовых строк фиксированной длины с автозаполнением нулями (например, "00БП-000015"). Если пользователь ввел на форме или передал из внешней интеграции число 15 или строку "15", обычный поиск не сопоставит его с реальным номером. Придется программно приводить номер к стандартному формату конфигурации, что усложняет код.

Решение 2. Использование запросов для надежного поиска (Рекомендуемое решение)

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

Проанализируем пример построения такого запроса:


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

// Передаем параметры. 
// Используем "%" для поиска номера, даже если он передан без лидирующих нулей
Запрос.УстановитьПараметр("Номер", "%" + СокрЛП(НомерСчета));
Запрос.УстановитьПараметр("Организация", Объект.Организация);
Запрос.УстановитьПараметр("Контрагент", Объект.Контрагент);

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

Преимущества данного подхода:

Решение 3. Архитектурный подход — ввод на основании

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

В платформе 1С для этого реализован стандартный механизм заполнения "На основании". Давайте посмотрим, как это сделать программно:


// 1. Получаем ссылку на исходный счет на оплату
СсылкаНаСчет = ВыбранныйСчетНаОплату; 

// 2. Создаем новый документ реализации
НоваяРеализация = Документы.РеализацияТоваровУслуг.СоздатьДокумент();

// 3. Устанавливаем дату и другие базовые реквизиты, если необходимо
НоваяРеализация.Дата = ТекущаяДата();

// 4. Используем стандартный метод заполнения на основании
НоваяРеализация.Заполнить(СсылкаНаСчет);

// 5. Записываем и проводим документ
НоваяРеализация.Записать(РежимЗаписиДокумента.Проведение);

Метод Заполнить() автоматически вызывает обработчик ОбработкаЗаполнения в модуле объекта документа РеализацияТоваровУслуг. При этом:

← На главную