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

Программист 1С v8.3 (Обычные формы) Управленческий учет Торговля и дистрибуция
← На главную

При разработке механизмов интеграции, например, при загрузке данных из внешних систем типа iiko или r-keeper, программисты часто сталкиваются с ситуацией, когда код «не видит» уже созданный документ. В результате система создает дубликаты, а отладчик при попытке просмотреть результат запроса выдает загадочное сообщение «ошибка чтения значения». Разберем по шагам, почему это происходит и как построить надежный алгоритм поиска.

Анализируем проблему точности типа Дата

В системе 1С:Предприятие тип Дата — это не просто календарный день, это комбинация даты и времени с точностью до секунды. Рассмотрим условие в запросе, которое использовал автор темы:


| ГДЕ
|     Приход.Дата = &Дата

Проанализируем ситуацию: если в параметре &Дата передается значение 01.10.2023 12:00:00, а документ в базе данных записан со временем 01.10.2023 12:00:05, запрос никогда не найдет этот документ. Разница всего в пять секунд делает условие равенства ложным. В автоматизированных системах время записи часто определяется моментом завершения транзакции или текущим временем сервера, поэтому точное совпадение — это скорее случайность, чем правило.

Решение: Для поиска документов в рамках одного дня всегда следует использовать проверку на вхождение в диапазон (от начала до конца дня). Изменим логику запроса, используя оператор МЕЖДУ или функцию НАЧАЛОПЕРИОДА.


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

// Вариант 2: Сравнение начал периодов
Запрос.Текст = 
"ВЫБРАТЬ Ссылка ИЗ Документ.ПриходнаяНакладная 
|ГДЕ НАЧАЛОПЕРИОДА(Дата, ДЕНЬ) = НАЧАЛОПЕРИОДА(&Дата, ДЕНЬ)
|И Контрагент = &Контрагент";

Разбираемся с арифметикой дат в 1С

Посмотрим на строку кода из примера: Приход.Дата = Дата - 6;. Здесь кроется критическая ошибка понимания типов. В языке 1С прибавление или вычитание числа из переменной типа Дата — это операция над секундами.

Вычитая 6, мы смещаем время документа всего на 6 секунд назад. Если автор хотел найти документ за «6 дней до...», расчет должен быть иным. В одном дне 86 400 секунд (24 * 60 * 60). Следовательно, для работы с днями нужно использовать соответствующие множители.

Проанализируем правильный подход к смещению дат:


// Если нужно вычесть 6 дней:
КоличествоДней = 6;
НужнаяДата = Дата - (КоличествоДней * 24 * 60 * 60);

// Но более современный и читаемый способ — использовать встроенную функцию:
НужнаяДата = ДобавитьМесяц(Дата, 0); // просто пример, для дней лучше ДобавитьМесяц не использовать
// В 8.3 рекомендуется:
НужнаяДата = НачалоДня(Дата) - 6 * 86400; 

Почему отладчик пишет «ошибка чтения значения»

Когда мы останавливаемся на точке останова и видим в Выборка.Ссылка ошибку чтения, это не всегда означает сбой платформы. Рассмотрим механизм работы объекта ВыборкаИзРезультатаЗапроса.

  1. Объект выборки — это курсор, который изначально стоит перед первой записью.
  2. До вызова метода Выборка.Следующий() никаких данных в полях выборки нет.
  3. Если запрос вернул пустой результат, вызов Следующий() вернет Ложь, и курсор не сдвинется на запись.

Посмотрим на фрагмент кода автора:


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

Выясним причину: если условие в Если не выполняется, значит запрос ничего не нашел. В блоке Иначе данных нет, и обращение к свойствам выборки нелегитимно. Это подтверждает, что проблема именно в условиях самого запроса (неверная дата, не найден контрагент или не совпал комментарий).

Особенности поиска по строковым полям (Комментарий)

Автор использует условие Приход.Комментарий ПОДОБНО &Комм. Использование решетки # и сложных строк при обмене данными с iiko может быть коварным. Проанализируем возможные риски:

Риски метода НайтиПоКоду

В коде мы видим: Контрагент = Справочники.Контрагенты.НайтиПоКоду("1-00008969");. Это опасная практика для тиражных решений или интеграций. Если в рабочей базе код контрагента изменится хотя бы на один символ (или в начале добавится префикс узла), метод вернет ПустаяСсылка.

Если Контрагент.Пустая(), то запрос по условию Приход.Контрагент = &Контрагент будет искать накладные с пустым контрагентом, что, очевидно, не приведет к результату. Рекомендуем всегда добавлять проверку после поиска:


Если Контрагент.Пустая() Тогда
    Сообщить("Внимание: Контрагент с кодом " + КодПоиска + " не найден в справочнике!");
    Возврат Истина; // Прекращаем выполнение с ошибкой
КонецЕсли;

Итоговые рекомендации по исправлению кода

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

  1. Используйте границы дня: Передавайте в параметры запроса НачалоДня(ДатаПоиска) и КонецДня(ДатаПоиска).
  2. Проверяйте параметры перед запросом: Убедитесь, что ссылки на контрагента и структурную единицу не пустые.
  3. Используйте консоль запросов: Скопируйте текст запроса в консоль, подставьте те же параметры и посмотрите, есть ли результат. Если в консоли пусто — проблема в данных или фильтрах.
  4. Учитывайте специфику общепита: Если документ создается «ночной» сменой, его фактическая дата может быть следующим днем относительно учетной даты в iiko. В таких случаях поиск нужно делать за период [Дата, Дата + 1 день].

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

← На главную