Почему метод НайтиПоНомеру() в 1С 7.7 не находит документ при использовании переменных?

Программист 1С v7.7 Бухгалтерский учет
← На главную

При разработке механизмов импорта данных, например, при разборе выписок из «Клиент-Банка» или обработке назначений платежей в платежных поручениях, программисты часто сталкиваются со странным поведением метода НайтиПоНомеру(). В этой статье мы подробно разберем ситуацию, когда поиск документа по строковому номеру и дате выдает «0» (не найдено), если значения передаются через переменные, но при этом отлично работает, если те же самые значения подставить в код вручную в виде текстовых литералов. Проанализируем причины этого явления и найдем надежное решение для платформы 1С:Предприятие 7.7.

Суть проблемы: переменные против литералов

Рассмотрим типичную ситуацию. Программист пытается найти документ, используя данные, извлеченные из текстовой строки. В отладчике («Табло») ситуация выглядит следующим образом:


ИщемНомер = "ТИ003965"
ИщемДату = '24.09.21'
// Попытка поиска через объект:
результат1 = объект.НайтиПоНомеру(ИщемНомер, ИщемДату); // Возвращает 0
// Попытка поиска прямой подстановкой значений:
результат2 = объект.НайтиПоНомеру("ТИ003965", '24.09.21'); // Возвращает 1

На первый взгляд это кажется нелогичным: значения в переменных визуально идентичны тем, что подставляются вручную. Однако корень проблемы кроется в том, как платформа 1С 7.7 обрабатывает типы данных и конкретно — формат года в датах.

Анализ работы конструктора Дата() в 1С 7.7

Выясним причину такого поведения, изучив механизм формирования даты. В 1С 7.7 существует два принципиально разных способа задания даты в коде:

  1. Литерал даты: Запись вида '24.09.21' (в одинарных кавычках). При компиляции этого выражения платформа обращается к системным настройкам (так называемое «Окно года»). Если в настройках указано, что 21 — это 2021 год, платформа автоматически подставит правильный век.
  2. Функция-конструктор Дата(): Когда мы программно собираем дату из чисел с помощью функции Дата(Год, Месяц, День), платформа ведет себя иначе. Если передать в параметр Год число 21, система воспримет это буквально. В итоге будет создана дата 24 сентября 0021 года нашей эры.

В этом и заключается основная ловушка. Когда мы пишем в табло отладки литерал, мы получаем дату 2021 года. Но когда код разбирает строку «24.09.21», выделяет оттуда число 21 и передает его в Дата(), поиск начинает искать документ в начале первого тысячелетия. Естественно, метод НайтиПоНомеру() возвращает 0, так как в 21-м году документов в вашей базе не существует.

Разбор практического примера кода

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


// Извлекаем дату из строки "сч.№ТИ003965 от 24.09.21"
Позиция_от_ = Найти(Назначение, " от ");
Если Позиция_от_ <> 0 Тогда
    СтрокаДаты = Сред(Назначение, Позиция_от_ + 4, 8); // Получаем "24.09.21"
    ПозицияТочки = Найти(СтрокаДаты, ".");
    День = Лев(СтрокаДаты, ПозицияТочки - 1);
    Остаток = Сред(СтрокаДаты, ПозицияТочки + 1);
    ПозицияТочки2 = Найти(Остаток, ".");
    Месяц = Лев(Остаток, ПозицияТочки2 - 1);
    Год = Сред(Остаток, ПозицияТочки2 + 1);
    
    // ОШИБКА: Если Год = 21, то Дата() вернет 0021 год
    ИщемДату = Дата(Число(Год), Число(Месяц), Число(День));
КонецЕсли;

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

В данном примере, если в строке год указан двумя цифрами, переменная ИщемДату примет значение, далекое от реальности. При этом визуально в Сообщить() это может выглядеть как "24.09.21", и программист не сразу заметит подвох.

Как правильно исправить формирование года

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

Вариант 1: Простое добавление префикса столетия

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


Год = Число(Сред(Остаток, ПозицияТочки2 + 1));
Если Год < 100 Тогда
    Год = 2000 + Год; // Превращаем 21 в 2021
КонецЕсли;
ИщемДату = Дата(Год, Число(Месяц), Число(День));

Вариант 2: Универсальный подход с учетом «Окна года»

Более грамотным подходом будет проверка порога (например, если год больше 50, то это 1900-е, если меньше — 2000-е), чтобы код корректно обрабатывал исторические данные или будущие периоды:


Год = Число(ГодИзСтроки);
Если Год < 100 Тогда
    Год = Год + ?(Год < 50, 2000, 1900);
КонецЕсли;

Дополнительные факторы, влияющие на работу НайтиПоНомеру()

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

1. Периодичность нумерации

В конфигураторе для каждого вида документа задается свойство «Периодичность» (Год, Квартал, Месяц, День или Непериодический). Метод НайтиПоНомеру(Номер, Дата) использует второй параметр именно для того, чтобы определить, в каком временном интервале искать указанный номер. Если периодичность установлена в «Год», а вы ошиблись с годом в дате (как в случае с 0021 годом), поиск будет производиться в пустом периоде.

2. Лидирующие нули и длина номера

Номера документов в 1С 7.7 — это строки фиксированной длины. Если в конфигураторе длина номера определена как 10 символов, то номер «ТИ3965» в базе фактически хранится как «ТИ00003965» или «0000ТИ3965» (в зависимости от префикса). При использовании НайтиПоНомеру() крайне важно передавать строку, точно соответствующую по длине и содержанию номеру в базе данных. Если вы извлекаете номер из текста как «ТИ3965», а в базе он «ТИ00003965», поиск вернет 0. Используйте функции дополнения строки нулями, если это необходимо.

3. Регистр символов и префиксы

Поиск в 1С 7.7 чувствителен к регистру. Номер «ти003965» (строчными) не будет равен номеру «ТИ003965» (прописными). При разборе текста из платежки рекомендуется всегда приводить извлеченный номер к верхнему регистру с помощью функции ВРег().

4. Особенности функции СокрЛП()

Будьте осторожны при использовании СокрЛП(). В строках, скопированных из банковских систем, могут встречаться «невидимые» символы, такие как неразрывный пробел (ASCII код 160). Обычная функция СокрЛП() его не удаляет, так как считает значащим символом. Если такой пробел попадет в конец или начало номера, строковое сравнение внутри метода НайтиПоНомеру() не сработает.

Рекомендованный алгоритм поиска

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

  1. Тщательно очищаем извлеченный номер от лишних пробелов и приводим к верхнему регистру.
  2. Проверяем длину номера и, если она меньше заданной в конфигурации, дополняем номер лидирующими нулями (учитывая положение префикса).
  3. При формировании даты обязательно проверяем количество цифр в году и приводим двухзначный год к четырехзначному (20xx).
  4. Инициализируем объект документа через СоздатьОбъект("Документ.ИмяВида").
  5. Вызываем НайтиПоНомеру(), передавая подготовленные переменные.

Соблюдение этих правил позволит избежать большинства «мистических» ошибок, когда поиск в 1С 7.7 отказывается работать с корректными на первый взгляд данными.

← На главную