Нередко разработчики 1С сталкиваются с ситуациями, которые на первый взгляд кажутся «магией»: один и тот же код в одной и той же базе выдает разные результаты в зависимости от того, выполняется ли он в облаке (на сервере) или в локальной копии (файловом варианте). Разберем конкретный кейс, связанный с выгрузкой банковских выписок, проанализируем причины такого поведения и найдем правильные пути решения задачи.
Рассмотрим ситуацию: внешняя обработка должна собрать данные по документам ПоступлениеНаРасчетныйСчет и записать их в текстовый файл. При запуске в облачной инфраструктуре по определенному расчетному счету файл всегда оказывается пустым, хотя данные в базе точно есть. Однако стоит выгрузить эту же базу в файловый вариант и запустить ту же обработку локально — всё работает корректно. Выясним причину этого явления.
Проанализируем фрагмент кода, который использовался для поиска расчетного счета:
Организация = Справочники.Организации.НайтиПоРеквизиту("ИНН", "000000000");
СчетОрганизации = Справочники.БанковскиеСчета.НайтиПоРеквизиту("НомерСчета", НомерСчета, , Организация);
Основная проблема здесь заключается в использовании метода НайтиПоРеквизиту. Рассмотрим подробнее, почему он может подвести:
НайтиПоРеквизиту возвращает «первый попавшийся» результат. На локальном компьютере этим результатом может оказаться актуальный счет, а на сервере — помеченный на удаление.Решение: Заменим ненадежный поиск на явный запрос с фильтрацией по состоянию объекта. Рассмотрим пример правильного получения ссылки на счет:
ЗапросСчет = Новый Запрос;
ЗапросСчет.УстановитьПараметр("НомерСчета", НомерСчета);
ЗапросСчет.УстановитьПараметр("Владелец", Организация);
ЗапросСчет.Текст =
"ВЫБРАТЬ ПЕРВЫЕ 1
| БанковскиеСчета.Ссылка КАК Ссылка
|ИЗ
| Справочник.БанковскиеСчета КАК БанковскиеСчета
|ГДЕ
| БанковскиеСчета.НомерСчета = &НомерСчета
| И БанковскиеСчета.Владелец = &Владелец
| И НЕ БанковскиеСчета.ПометкаУдаления";
РезультатЗапроса = ЗапросСчет.Выполнить();
Если НЕ РезультатЗапроса.Пустой() Тогда
Выборка = РезультатЗапроса.Выбрать();
Выборка.Следующий();
СчетОрганизации = Выборка.Ссылка;
КонецЕсли;
Посмотрим на структуру основного запроса, который выбирает документы. В исходном коде часто встречается конструкция ПоступленияНаРасчетныйСчет.Ссылка.Дата или ПоступленияНаРасчетныйСчет.Ссылка.Организация. Проанализируем, чем это плохо:
Когда мы обращаемся через точку от поля Ссылка в таблице самого документа, мы заставляем SQL-сервер выполнять лишнее левое соединение (LEFT JOIN) таблицы с самой собой. Это не только замедляет работу, но и в редких случаях при сложных структурах данных может влиять на план выполнения запроса СУБД.
Решение: Обращайтесь к реквизитам документа напрямую из таблицы. Вместо ПоступленияНаРасчетныйСчет.Ссылка.Организация используйте ПоступленияНаРасчетныйСчет.Организация. Правильный фрагмент условий в запросе будет выглядеть так:
ГДЕ
ПоступленияНаРасчетныйСчет.Дата МЕЖДУ &ДатаС И &ДатаПо
И ПоступленияНаРасчетныйСчет.Организация = &Организация
И ПоступленияНаРасчетныйСчет.СчетОрганизации = &СчетОрганизации
В исходном коде автора для определения конца периода использовалась конструкция: НачалоДня(ДатаПоступленийПо) + 60 * 60 * 24. Рассмотрим подробнее, к чему это приводит.
Такой расчет дает нам 00:00:00 следующего дня. Если в условии запроса используется МЕЖДУ, то документы, созданные ровно в полночь следующего дня, попадут в выборку, что логически неверно. Если же нужно строго ограничить текущий день, в 1С предусмотрена встроенная функция КонецДня().
Однако при работе с регистрами (если вы решите перейти от запроса к документам к запросу по движениям) рекомендуется использовать объект Граница. Это гарантирует, что документы, записанные в последнюю секунду периода, будут включены или исключены корректно. Посмотрим на пример:
КонецПериода = Новый Граница(КонецДня(ДатаПоступленийПо), ВидГраницы.Включая);
Запрос.УстановитьПараметр("ДатаПо", КонецПериода);
Выясним, почему запрос к документам может выдавать пустой результат, в то время как консоль запросов показывает данные. При работе с документами мы выбираем все записи, включая непроведенные и помеченные на удаление (если не указали обратное). Если задача — получить данные о реальном поступлении денег, правильнее использовать регистры бухгалтерии или регистры накопления.
Разница между локальной и серверной базой здесь может проявиться в актуальности итогов. На сервере итоги могут быть не рассчитаны или кэшированы, в то время как локальная база при загрузке часто инициирует пересчет. Использование виртуальных таблиц оборотов регистров — более надежный способ получения финансовых данных.
ЖурналРегистрации или через Сообщить(). Проверьте типы значений: иногда СчетОрганизации может оказаться строкой или битой ссылкой, что визуально в отладке выглядит похоже.Дата, а ссылка — это Ссылка нужного типа.Соблюдение этих простых правил проектирования позволит избежать «магических» ошибок при миграции кода из файлового режима в клиент-серверный и сделает ваши обработки надежными в любой среде эксплуатации.