В практике разработчика на платформе 1С:Предприятие 7.7 часто возникает классическая задача: имея на руках основной документ (например, «Заявку покупателя» или «Реализацию»), необходимо «спуститься» вниз по иерархии и собрать данные из документов, которые были созданы на его основании. Это особенно актуально в современных реалиях, когда в типовые конфигурации прошлых лет приходится интегрировать механизмы работы с маркировкой («Честный ЗНАК»), где коды маркировки часто хранятся в отдельных подчиненных актах или спецификациях.
Разберем подробно, как реализовать этот механизм, проанализируем основные методы и рассмотрим нюансы, которые помогут избежать типичных ошибок при работе с текстовыми файлами обмена.
Для решения задачи получения списка документов, созданных на основании конкретного объекта, в языке 1С 7.7 предназначен метод ВыбратьПодчиненныеДокументы. Рассмотрим его синтаксис и особенности применения.
Синтаксис метода выглядит следующим образом:
ВыбратьПодчиненныеДокументы(Дата1, Дата2, Докум)
Проанализируем параметры:
Важный нюанс: этот метод можно вызывать только для объекта, созданного через СоздатьОбъект("Документ"). Посмотрим на примере, как правильно организовать цикл обхода.
// Создаем переменные для работы
Дл = СоздатьОбъект("Документ");
Подч = СоздатьОбъект("Документ");
// Предположим, ТекущийДок — это наша Заявка
Если Дл.НайтиДокумент(ТекущийДок) = 1 Тогда
// Инициируем выборку подчиненных
Если Подч.ВыбратьПодчиненныеДокументы(,, Дл.ТекущийДокумент()) = 1 Тогда
Пока Подч.ПолучитьДокумент() = 1 Цикл
// На каждой итерации переменная Подч содержит ссылку на документ
Сообщить("Найден документ: " + Подч.Вид() + " № " + Подч.НомерДок);
КонецЦикла;
КонецЕсли;
КонецЕсли;
Поскольку метод ВыбратьПодчиненныеДокументы возвращает все документы, связанные с основанием (это могут быть и счета, и накладные, и кассовые ордера), нам необходимо выделить только нужные. Рассмотрим ситуацию, когда нам требуется достать коды маркировки из специфического документа.
Проанализируем код с проверкой вида документа:
Пока Подч.ПолучитьДокумент() = 1 Цикл
// Проверяем, является ли документ нужным нам типом
Если Подч.Вид() = "ПриходнаяНакладная" Тогда
// Теперь мы можем обратиться к реквизитам или табличной части
Подч.ВыбратьСтроки();
Пока Подч.ПолучитьСтроку() = 1 Цикл
КодМаркировки = Подч.Маркировка;
// Обработка кода...
КонецЦикла;
КонецЕсли;
КонецЦикла;
Обратите внимание: после вызова ПолучитьДокумент() объект Подч уже позиционирован на конкретном документе. Нам не нужно дополнительно вызывать НайтиДокумент, мы сразу получаем доступ ко всем его реквизитам шапки и строкам табличной части.
Рассмотрим практическую ситуацию: нам необходимо выгрузить данные для торговой точки в формате TXT. В этот файл должны попасть не только основные реквизиты поступления, но и коды DataMatrix, которые хранятся в подчиненном документе.
Выясним причины, по которым это может быть сложно. Коды маркировки содержат спецсимволы, такие как GS (ASCII 29). Стандартный объект 1С Текст иногда может интерпретировать их неверно при записи в определенных кодировках.
Рассмотрим алгоритм формирования строки для файла:
Т = СоздатьОбъект("Текст");
// ... основной цикл по документам ...
СтрокаВыгрузки = СокрЛП(Док.Товар.Наименование) + "|" + КодМаркировки;
Т.ДобавитьСтроку(СтрокаВыгрузки);
// ...
Т.Записать(ПутьКФайлу);
Если принимающая сторона требует кодировку UTF-8, помните, что встроенный объект Текст в 7.7 работает преимущественно с Windows-1251 (ANSI). В таких случаях мы рекомендуем использовать внешние компоненты (например, v7plus.dll) или сторонние утилиты для конвертации готового файла.
Если ваша база данных работает на MS SQL Server и структура подчиненности очень разветвленная, стандартный метод ВыбратьПодчиненныеДокументы может работать медленно, так как он последовательно перебирает записи в системных таблицах связей.
Проанализируем альтернативный путь — использование прямых запросов через компоненту 1С++. Это позволяет одним махом получить данные из всей цепочки документов. В SQL-версии 7.7 связи хранятся в таблице _1CRUMMS. Прямой запрос к этой таблице по PARENTID (ID документа-основания) отрабатывает в десятки раз быстрее штатных средств при больших объемах данных.
Бывают случаи, когда «Заявка» порождает «Реализацию», а та, в свою очередь, — «Документ с кодами маркировки». В такой ситуации прямой вызов ВыбратьПодчиненныеДокументы от «Заявки» не увидит документ с кодами, так как он находится на втором уровне вложенности.
Разберем, как реализовать рекурсивный поиск:
Процедура НайтиМаркировкуРекурсивно(ТекДок)
Сп = СоздатьОбъект("Документ");
Если Сп.ВыбратьПодчиненныеДокументы(,, ТекДок.ТекущийДокумент()) = 1 Тогда
Пока Сп.ПолучитьДокумент() = 1 Цикл
Если Сп.Вид() = "ДокументМаркировки" Тогда
// Нашли то, что искали!
ОбработатьКоды(Сп.ТекущийДокумент());
Иначе
// Идем глубже в дерево
НайтиМаркировкуРекурсивно(Сп.ТекущийДокумент());
КонецЕсли;
КонецЦикла;
КонецЕсли;
КонецПроцедуры
Важное предостережение: при использовании рекурсии всегда следите за тем, чтобы в базе не было «зацикленных» документов (когда Док1 ссылается на Док2, а Док2 на Док1), иначе это приведет к переполнению стека и вылету программы. В 1С 7.7 такая ситуация встречается редко, но при программном создании документов ее исключать нельзя.
Мы проанализировали основные инструменты для работы со структурой подчиненности. Для большинства задач достаточно стандартного метода ВыбратьПодчиненныеДокументы. Если же вы сталкиваетесь с задачами маркировки, обратите особое внимание на корректность записи спецсимволов в результирующий текстовый файл и, при необходимости, используйте рекурсивные алгоритмы для обхода всех уровней дерева документов.
Совместное использование этих подходов позволит вам эффективно модернизировать старые системы под современные требования учета без глобальной переработки архитектуры конфигурации.