Как правильно составить запрос по документам в 1С 7.7 и сопоставить контрагентов из внешнего списка?

Программист 1С v7.7 IT и автоматизация бизнеса
← На главную

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

Часть 1: Особенности построения запросов по документам в 1С 7.7

Начнем с первой проблемы. Автор попытался выбрать документы ПКО за период и вывести их реквизиты в таблицу. Однако его первоначальный запрос возвращал пустой результат. Давайте проанализируем причины этой ошибки и разберем по шагам, как устроен механизм «черных» запросов в 1С 7.7.

Основная ошибка в исходном коде автора заключалась в полном отсутствии группировок. В платформе 1С 7.7 метод Запрос.Группировка() жестко завязан на синтаксическую конструкцию Группировка в самом тексте запроса. Если в запросе не объявлена хотя бы одна группировка (например, по документу), то обход результатов через цикл Пока Запрос.Группировка(...) = 1 Цикл попросту не начнется — метод сразу вернет значение 0.

Помимо этого, существует распространенный миф о том, что в запросах по документам в 1С 7.7 обязательно нужно использовать агрегатные функции (такие как Сумма или Количество). На самом деле это не так. Функции обязательны только тогда, когда вам необходимо рассчитывать итоги по группировкам. Если же вы используете директиву Без итогов;, вы можете спокойно получать любые реквизиты документов без объявления искусственных функций.

Также крайне важно использовать правильные директивы фильтрации. Инструкция Обрабатывать НеПомеченныеНаУдаление предназначена исключительно для справочников. При работе с документами платформа ее проигнорирует. Вместо этого для документов применяется директива ОбрабатыватьДокументы.

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


Перем Запрос, ТекстЗапроса, Таб;

Запрос = СоздатьОбъект("Запрос");
ТекстЗапроса = 
"//{{ЗАПРОС(ПКО)
|Период с ВыбНачПериода по ВыбКонПериода;
|ОбрабатыватьДокументы Все;
|Без итогов;
|ПринятоОт = Документ.ПриходныйОрдер.ПринятоОт;
|Основание = Документ.ПриходныйОрдер.Основание;
|КоррСчет = Документ.ПриходныйОрдер.КоррСчет;
|Сумма = Документ.ПриходныйОрдер.Сумма;
|ТекущийДокумент = Документ.ПриходныйОрдер.ТекущийДокумент;
|Группировка ТекущийДокумент;
|"//}}ЗАПРОС
;

Если Запрос.Выполнить(ТекстЗапроса) = 0 Тогда
    Предупреждение("Ошибка выполнения запроса!");
    Возврат;
EndIf;

Как вывести произвольные реквизиты в макет таблицы

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

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

  1. Откройте макет вашей таблицы (отчета) в режиме Конфигуратора.
  2. Найдите строку (секцию), которая выводится внутри цикла обхода запроса (в нашем примере это секция ПКО).
  3. Кликните дважды на ячейку, в которую вы хотите вывести, например, содержание поля «Основание».
  4. В открывшемся окне свойств ячейки на вкладке «Текст» установите тип значения «Выражение» (или укажите тип «Шаблон»).
  5. В поле ввода выражения вручную пропишите обращение к объекту запроса: Запрос.Основание. Соответственно, для корреспондирующего счета выражение будет выглядеть как Запрос.CorrСчет.

После этого запустите отчет в режиме 1С:Предприятие — все выбранные реквизиты будут корректно заполняться при выводе каждой строки.

Часть 2: Сопоставление контрагентов из внешнего списка с базой 1С

Перейдем ко второй задаче, описанной автором: имеется внешний список из 800-900 наименований контрагентов (например, в файле Excel или текстовом файле TXT), и для них необходимо найти соответствующие договоры в базе данных 1С 7.7. Ситуация усложняется тем, что сопоставление нужно производить по реквизиту НаименованиеПолное.

«Тупой построчный перебор» (когда для каждой строки файла запускается цикл полного обхода справочника через метод ВыбратьЭлементы()) — это самый неэффективный способ решения задачи. Если в вашей базе содержится 5000 контрагентов, а в файле — 900 строк, вашей программе придется выполнить около 4.5 миллионов итераций сравнения в памяти. На файловой базе данных DBF это приведет к зависанию программы на длительное время.

Рассмотрим два профессиональных и быстрых способа решения этой задачи.

Способ №1: Кэширование справочника в Таблицу Значений (ТЗ)

Суть метода заключается в том, что мы выполняем всего один-единственный проход по справочнику Контрагентов в самом начале работы алгоритма. Мы считываем ссылки на элементы и их полное наименование во временную таблицу значений, которая будет храниться в оперативной памяти компьютера. Поиск по индексам ТЗ в памяти работает в сотни раз быстрее, чем постоянные обращения к жесткому диску и файлам базы данных.

Давайте разберем этот алгоритм по шагам на примере кода:


// Шаг 1. Создаем и подготавливаем Таблицу Значений для кэша
ТЗКэш = СоздатьОбъект("ТаблицаЗначений");
ТЗКэш.НоваяКолонка("Контрагент");
ТЗКэш.НоваяКолонка("ПолнНаименование", "Строка");

// Шаг 2. Заполняем кэш данными из базы за один проход
СпрКонтр = СоздатьОбъект("Справочник.Контрагенты");
СпрКонтр.ВыбратьЭлементы();
Пока СпрКонтр.ПолучитьЭлемент() = 1 Цикл
    Если СпрКонтр.ЭтоГруппа() = 1 Тогда
        Продолжить; // Группы справочника нам не нужны
    КонецЕсли;
    
    ТЗКэш.НоваяСтрока();
    ТЗКэш.Контрагент = СпрКонтр.ТекущийЭлемент();
    
    // ВАЖНО: Приводим строку к верхнему регистру и удаляем лишние пробелы для надежного сравнения
    ТЗКэш.ПолнНаименование = ВРег(СокрЛП(СпрКонтр.ПолноеНаименование));
КонецЦикла;

// Шаг 3. Читаем внешний файл и быстро ищем совпадения в кэше
// (В данном примере имитируем чтение строк из файла в цикле)
Пока Файл.КонецВыборки() = 0 Цикл
    СтрокаИзФайла = Файл.ПолучитьСтроку(); // Получаем очередное наименование из внешнего источника
    ПоисковоеЗначение = ВРег(СокрЛП(СтрокаИзФайла));
    
    НомСтрокиТЗ = 0;
    // Используем встроенный быстрый поиск по колонке Таблицы Значений
    Если ТЗКэш.НайтиЗначение(ПоисковоеЗначение, НомСтрокиТЗ, "ПолнНаименование") = 1 Тогда
        НайденныйКонтрагент = ТЗКэш.ПолучитьЗначение(НомСтрокиТЗ, "Контрагент");
        
        // Теперь мы можем легко получить договоры этого контрагента
        СпрДоговоры = СоздатьОбъект("Справочник.Договоры");
        СпрДоговоры.ИспользоватьВладельца(НайденныйКонтрагент);
        СпрДоговоры.ВыбратьЭлементы();
        Пока СпрДоговоры.ПолучитьЭлемент() = 1 Цикл
            // Выводим информацию о найденных договорах в отчет
            Сообщить("Контрагент: " + НайденныйКонтрагент + " -> Договор: " + СпрДоговоры.ТекущийЭлемент());
        КонецЦикла;
    Иначе
        Сообщить("ВНИМАНИЕ: Не найден контрагент с наименованием: " + СтрокаИзФайла);
    КонецЕсли;
КонецЦикла;

Способ №2: Индексация реквизита в Конфигураторе

Если вы имеете доступ к изменению структуры метаданных конфигурации, вы можете настроить базу данных так, чтобы поиск по реквизиту ПолноеНаименование выполнялся практически мгновенно на уровне СУБД (или DBF-индексов).

Для этого выполните следующие действия:

  1. Откройте базу данных в режиме Конфигуратора.
  2. Найдите справочник Контрагенты и перейдите к списку его реквизитов.
  3. Откройте свойства реквизита, в котором хранится полное наименование (например, НаименованиеПолное или ПолнНаименование). Убедитесь, что этот реквизит имеет тип «Строка» и не является периодическим.
  4. В окне свойств реквизита установите галочку в поле «Сортировка».
  5. Сохраните изменения и выполните реструктуризацию базы данных.

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


СпрКонтр = СоздатьОбъект("Справочник.Контрагенты");
СтрокаИзФайла = "ООО ""Вектор"""; // Пример строки из внешнего файла

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

Важные практические советы по сопоставлению строк

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

← На главную