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