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

Программист 1С v8.3 (Управляемые формы) Бухгалтерский учет Сфера услуг, туризм и социальный сектор
← На главную

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

Суть проблемы: когда код ведет себя непредсказуемо

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

Причина №1: Коварство метода НайтиПоРеквизиту

Проанализируем фрагмент кода, который использовался для поиска расчетного счета:


Организация = Справочники.Организации.НайтиПоРеквизиту("ИНН", "000000000");
СчетОрганизации = Справочники.БанковскиеСчета.НайтиПоРеквизиту("НомерСчета", НомерСчета, , Организация);

Основная проблема здесь заключается в использовании метода НайтиПоРеквизиту. Рассмотрим подробнее, почему он может подвести:

  1. Отсутствие фильтра по пометке удаления: Метод ищет среди всех записей справочника. Если в базе заведены два счета с одинаковым номером (например, один был ошибочно создан или закрыт, а затем заведен новый с тем же номером), метод может вернуть ссылку на помеченный на удаление объект.
  2. Различия в СУБД: В файловом варианте (локально) и в клиент-серверном варианте (SQL) механизмы индексации и физического хранения данных различаются. При наличии дублей в базе НайтиПоРеквизиту возвращает «первый попавшийся» результат. На локальном компьютере этим результатом может оказаться актуальный счет, а на сервере — помеченный на удаление.

Решение: Заменим ненадежный поиск на явный запрос с фильтрацией по состоянию объекта. Рассмотрим пример правильного получения ссылки на счет:


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

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

Причина №2: Ошибки при разыменовании полей в запросе

Посмотрим на структуру основного запроса, который выбирает документы. В исходном коде часто встречается конструкция ПоступленияНаРасчетныйСчет.Ссылка.Дата или ПоступленияНаРасчетныйСчет.Ссылка.Организация. Проанализируем, чем это плохо:

Когда мы обращаемся через точку от поля Ссылка в таблице самого документа, мы заставляем SQL-сервер выполнять лишнее левое соединение (LEFT JOIN) таблицы с самой собой. Это не только замедляет работу, но и в редких случаях при сложных структурах данных может влиять на план выполнения запроса СУБД.

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


ГДЕ
    ПоступленияНаРасчетныйСчет.Дата МЕЖДУ &ДатаС И &ДатаПо
    И ПоступленияНаРасчетныйСчет.Организация = &Организация
    И ПоступленияНаРасчетныйСчет.СчетОрганизации = &СчетОрганизации

Причина №3: Границы периодов и «миллисекунды»

В исходном коде автора для определения конца периода использовалась конструкция: НачалоДня(ДатаПоступленийПо) + 60 * 60 * 24. Рассмотрим подробнее, к чему это приводит.

Такой расчет дает нам 00:00:00 следующего дня. Если в условии запроса используется МЕЖДУ, то документы, созданные ровно в полночь следующего дня, попадут в выборку, что логически неверно. Если же нужно строго ограничить текущий день, в 1С предусмотрена встроенная функция КонецДня().

Однако при работе с регистрами (если вы решите перейти от запроса к документам к запросу по движениям) рекомендуется использовать объект Граница. Это гарантирует, что документы, записанные в последнюю секунду периода, будут включены или исключены корректно. Посмотрим на пример:


КонецПериода = Новый Граница(КонецДня(ДатаПоступленийПо), ВидГраницы.Включая);
Запрос.УстановитьПараметр("ДатаПо", КонецПериода);

Причина №4: Документы vs Регистры

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

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

Рекомендации для стабильной работы кода

  1. Откажитесь от НайтиПоРеквизиту в пользу запросов. Это даст вам полный контроль над тем, какие объекты попадают в результат (фильтр по удалению, по владельцу, по дополнительным признакам).
  2. Проверяйте данные на дубли. Если поиск по ИНН или номеру счета возвращает несколько активных объектов, это повод для сообщения об ошибке в данных, а не для случайного выбора одного из них.
  3. Логируйте параметры. Если «магия» продолжается, перед выполнением запроса выведите все установленные параметры в ЖурналРегистрации или через Сообщить(). Проверьте типы значений: иногда СчетОрганизации может оказаться строкой или битой ссылкой, что визуально в отладке выглядит похоже.
  4. Используйте явную типизацию. В облачных базах (особенно на PostgreSQL) СУБД более чувствительна к типам параметров. Убедитесь, что передаваемая дата — это именно Дата, а ссылка — это Ссылка нужного типа.

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

← На главную