Почему метод НайтиПоНомеру возвращает пустую ссылку или 0 при попытке получить количество элементов табличной части?

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

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

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


Процедура КнопкаВыполнитьНажатие(Кнопка)
    //Здесь получаем 9223 (через запрос)
    Запрос = новый Запрос;
    Запрос.Текст = "ВЫБРАТЬ
                   |    СД.Ссылка
                   |ИЗ
                   |    Документ.СД КАК СД
                   |ГДЕ
                   |    СД.Номер = ""МК0000000000028""";
    Результат = Запрос.Выполнить();
    Выборка = Результат.Выбрать();
    Пока Выборка.Следующий() Цикл
        count = Выборка.Ссылка.Договоры.Количество(); // Получаем количество элементов табличной части "Договоры"
    КонецЦикла;

    //Вот здесь получаем 0 (через НайтиПоНомеру)
    Док = Документы.СД.НайтиПоНомеру("МК0000000000028");
    count2 = Док.Договоры.Количество(); // Пытаемся получить количество, но Док = Неопределено
    Сообщить("count = " + count + "; count2 = " + count2);
КонецПроцедуры

Как видим из приведенного примера, автор пытается получить количество строк табличной части Договоры. В первом случае, используя запрос, он успешно получает значение 9223. Во втором случае, используя метод НайтиПоНомеру(), он получает 0. Это происходит потому, что переменная Док после вызова НайтиПоНомеру() оказывается Неопределено, а попытка обратиться к ее свойствам (например, Док.Договоры) приводит к ошибке или, как в данном случае, к отсутствию результата (если ошибка не перехвачена, а Количество() у Неопределено возвращает 0 или подобное поведение).

В чем причина такого поведения?

Основная причина кроется в механизме поиска документов по номеру в 1С:Предприятии, а именно в таком понятии, как периодическая нумерация документов. Разберем подробнее:

  1. Периодическая нумерация документов: В 1С для многих типов документов можно настроить периодичность нумерации. Это означает, что номер документа может быть уникальным не во всей базе, а только в пределах определенного периода — например, года, месяца или дня. Типичный пример: документ "Счет №1" может существовать в январе 2023 года и "Счет №1" может существовать в январе 2024 года. Если номер является уникальным только в пределах, скажем, года, то в разные годы могут быть документы с одинаковыми номерами.
  2. Метод НайтиПоНомеру() и его параметры: Метод НайтиПоНомеру() имеет два основных параметра: Номер (обязательный) и Дата (необязательный). Документы.ИмяДокумента.НайтиПоНомеру(Номер, Дата)
    • Параметр Номер: Строковое представление номера документа, который мы ищем.
    • Параметр Дата: Дата из интервала, в котором производится поиск по номеру. Этот параметр становится критически важным, если для типа документа установлена периодическая нумерация. Если дата не указана, система может не знать, в каком именно периоде искать документ с указанным номером.
  3. Поведение без указания даты: Если вы не указываете параметр Дата для документа с периодической нумерацией, метод НайтиПоНомеру() может вернуть Неопределено. Это происходит потому, что система не может однозначно идентифицировать, какой именно документ с данным номером вы ищете, если таких документов несколько в разных периодах. Она не может "угадать" нужный период поиска. В отладчике мы бы увидели, что Док действительно содержит значение ДокументСсылка.СД: <Объект не найден> или просто Неопределено, что и подтверждает сообщение 6 из форума.
  4. Поведение запроса: Запрос, напротив, работает иначе. Когда мы пишем ГДЕ СД.Номер = "МК0000000000028", запрос просто перебирает все записи в таблице документов Документ.СД и ищет совпадение по полю Номер. Он не учитывает настройки периодической нумерации на уровне конфигурации, если это явно не прописано в условиях запроса (например, И СД.Дата МЕЖДУ ...). Если в базе есть хотя бы один документ с таким номером, запрос его найдет. Если таких документов несколько (в разных периодах), запрос вернет все найденные ссылки, и при использовании цикла Пока Выборка.Следующий() Цикл мы будем работать с каждым из них по очереди. В данном случае, судя по сообщению 10 (Выборка.Количество = 1), запрос нашел только один документ с указанным номером, что могло привести к ложному ощущению, что проблема не в нумерации. Однако, НайтиПоНомеру без даты все равно не справился.

Решение проблемы: Укажите дату в методе НайтиПоНомеру()

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

Рассмотрим, как изменить исходный код:


Процедура КнопкаВыполнитьНажатие(Кнопка)
    // Здесь получаем 9223 (через запрос) - этот участок кода можно оставить без изменений
    Запрос = новый Запрос;
    Запрос.Текст = "ВЫБРАТЬ
                   |    СД.Ссылка,
                   |    СД.Дата // Добавим дату в выборку, чтобы использовать ее для поиска
                   |ИЗ
                   |    Документ.СД КАК СД
                   |ГДЕ
                   |    СД.Номер = ""МК0000000000028""";
    Результат = Запрос.Выполнить();
    Выборка = Результат.Выбрать();
    
    СсылкаНаДокумент = Неопределено;
    ДатаДокумента = Неопределено;
    
    Если Выборка.Следующий() Тогда // Предполагаем, что запрос находит один документ
        СсылкаНаДокумент = Выборка.Ссылка;
        ДатаДокумента = Выборка.Дата;
        count = СсылкаНаДокумент.Договоры.Количество();
    Иначе
        count = 0;
    КонецЕсли;

    // Вот здесь исправляем, указывая дату
    Если ДатаДокумента <> Неопределено Тогда
        Док = Документы.СД.НайтиПоНомеру("МК0000000000028", ДатаДокумента);
        Если Док <> Неопределено Тогда
            count2 = Док.Договоры.Количество();
        Иначе
            count2 = 0; // Документ не найден даже с датой (крайне редкий случай при корректной дате)
        КонецЕсли;
    Иначе
        count2 = 0; // Дата не была найдена запросом
    КонецЕсли;
    
    Сообщить("count = " + count + "; count2 = " + count2);
КонецПроцедуры

В этом примере мы сначала получаем дату документа с помощью запроса. Затем эту дату мы передаем в метод НайтиПоНомеру(). Теперь, когда система знает, в каком временном интервале искать документ с указанным номером, она успешно находит нужную ссылку, и мы можем получить количество элементов табличной части.

Даже если мы не знаем точную дату документа заранее, но знаем, что он был создан, например, в текущем году или в определенном месяце, мы можем передать любую дату из этого периода:


// Пример: если знаем, что документ был в 2024 году
ДатаПоиска = Дата(2024, 1, 1); // Можно взять любую дату из 2024 года
Док = Документы.СД.НайтиПоНомеру("МК0000000000028", ДатаПоиска);
Если Док <> Неопределено Тогда
    count2 = Док.Договоры.Количество();
Иначе
    Сообщить("Документ не найден с датой " + ДатаПоиска);
КонецЕсли;

Важные нюансы и дополнительные советы

1. Поведение НайтиПоНомеру() при дубликатах

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

2. Когда использовать запрос вместо НайтиПоНомеру()

Метод НайтиПоНомеру() удобен, когда вы уверены в уникальности номера в рамках определенного периода и знаете дату. Однако, если:

В этих случаях запрос является более мощным и гибким инструментом. Он позволяет точно настроить условия выборки и получить нужные данные.


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

Результат = Запрос.Выполнить();
Выборка = Результат.Выбрать();
Пока Выборка.Следующий() Цикл
    // Работаем с каждым найденным документом
    Сообщить("Найден документ: " + Выборка.Ссылка + ", количество договоров: " + Выборка.Ссылка.Договоры.Количество());
КонецЦикла;

3. Поиск по уникальному идентификатору (УИД)

Если у вас есть уникальный идентификатор документа (UUID), это самый надежный способ получить ссылку на документ, так как УИД гарантированно уникален во всей базе. Метод НайтиПоУникальномуИдентификатору() не требует никаких дополнительных параметров, кроме самого УИД:


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

Заключение

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

Помните, что 1С предоставляет множество инструментов для работы с данными, и выбор подходящего метода (НайтиПоНомеру(), запрос, поиск по УИД) зависит от конкретной задачи и особенностей вашей конфигурации.

← На главную