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

Программист 1С v7.7 Управленческий учет Промышленность, строительство и АПК
← На главную

При разработке отчетов в 1С:Предприятии 7.7 часто возникает задача вывода актуальных данных по объекту или договору, например, даты начала и окончания работ. Нередко эти сроки могут меняться в дополнительных соглашениях. Если пытаться получить данные через стандартный запрос к регистрам, можно столкнуться с ситуацией, когда данные из соглашений выводятся только в том случае, если документ попадает в выбранный интервал отчета, либо не выводятся вовсе. Разберем причины такого поведения и способы правильного построения выборки данных.

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

Анализ проблемы и ограничений стандартных запросов 1С 7.7

Выясним причину, почему исходный запрос к регистру возвращает не те данные или теряет документы соглашений:

  1. Ограничение периода запроса: Конструкция Период С НачПер По ОконПер; во встроенном языке запросов 1С 7.7 жестко отсекает движения регистров. Если дополнительное соглашение было проведено до начала или после окончания указанного интервала, его движения и сам документ не попадут в выборку.
  2. Избыточные и некорректные группировки: Наличие группировок по незаполненным измерениям или дублирующим полям (например, одновременные группировки по измерениям, реквизитам и регистратору) перегружает выборку и усложняет обход дерева результатов в цикле.
  3. Ошибки архитектуры регистров: Запись ссылки на документ в измерение остаточного регистра приводит к тому, что регистр перестает сходиться «в ноль», накапливает избыточные итоги и снижает производительность системы.

Посмотрим на пример исходного запроса, содержащего неоптимальные группировки и логику выборки:


ТекстЗапроса = 
"
|Период С НачПер По ОконПер;
|ТекДок = Регистр.Обороты.ТекущийДокумент;
|Документы = Регистр.Обороты.Документ;
|СуммаДог = Регистр.Обороты.СуммаДог;
|_Объект = Регистр.Обороты.Объект; 
|Объект = Регистр.Обороты.Объект.Наименование; 
|Отдел = Регистр.Обороты.Объект.Отдел;
|Группировка Отдел Упорядочить По Отдел.Код;
|Группировка Документы;
|Группировка Объект; 
|Группировка ТекДок; 
|Функция ОбщСуммаДог = Приход(СуммаДог);
|";

Проанализируем этот код: группировка Документы и группировка Отдел, если они не заполняются в регистраторе или не используются для иерархического вывода, являются лишними. Попытка внутри цикла по ТекДок отловить даты документа не сработает для соглашений за рамками периода.

Способ 1. Получение данных последнего документа через выборку по реквизиту

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

Если в метаданных документа ДопСоглашение у реквизита Объект установлен признак «Отбор по реквизиту», мы можем быстро получить самый свежий документ с помощью обратного порядка выборки.

Посмотрим на пример функции для получения актуальных дат:


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

Способ 2. Оптимизация структуры запроса и обхода группировок

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

Рассмотрим оптимизированный текст запроса:


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

Если Запрос.Выполнить(ТекстЗапроса) = 0 Тогда
    Возврат;
КонецЕсли;

Разберем алгоритм обхода результатов:

  1. На уровне группировки Объект инициализируем переменные дат базовыми значениями из реквизитов объекта или связанного договора.
  2. Во вложенном цикле по регистраторам (ТекДок) проверяем тип документа функцией Вид().
  3. Если документ относится к виду "ДопСоглашение", переопределяем переменные дат новыми значениями. Поскольку в 1С 7.7 документы внутри группировки упорядочены хронологически, последняя итерация цикла запишет самые свежие даты.

Посмотрим на реализацию вывода данных в таблицу:


Пока Запрос.Группировка(1) = 1 Цикл
    ТекОбъект = Запрос.Объект;
    
    // Значения по умолчанию из договора объекта
    ДатаНач = ТекОбъект.Договор.НачПроект;
    ДатаОкон = ТекОбъект.Договор.ОконПроект;
    Сумма = 0;
    
    Пока Запрос.Группировка(2) = 1 Цикл
        Док = Запрос.ТекДок;
        
        Если Док.Вид() = "ДоговорНаПроектирование" Тогда
            Сумма = Запрос.СуммаДог;
        ИначеЕсли Док.Вид() = "ДопСоглашение" Тогда
            Если ПустоеЗначение(Док.НачПроект) = 0 Тогда
                ДатаНач = Док.НачПроект;
            КонецЕсли;
            Если ПустоеЗначение(Док.ОконПроект) = 0 Тогда
                ДатаОкон = Док.ОконПроект;
            КонецЕсли;
            Сумма = Док.Сумма;
        КонецЕсли;
    КонецЦикла;
    
    Таб.ВывестиСекцию("СтрокаДанных");
КонецЦикла;

Способ 3. Использование периодических реквизитов

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

В этом случае при проведении документов ДоговорНаПроектирование и ДопСоглашение в модуле документа выполняется запись истории реквизита с помощью метода УстановитьРеквизитПоДокументу(). Тогда в любом отчете актуальные даты на произвольный момент времени извлекаются без перебора документов через объект Периодический:


ПериодРекв = СоздатьОбъект("Периодический");
ПериодРекв.ИспользоватьОбъект("ДатаОкончания", ТекОбъект);
АктуальнаяДата = ПериодРекв.Получить(ОконПер);

Важные рекомендации:

  1. Не следует объявлять объекты без присвоения переменным (конструкции вроде СоздатьОбъект("Регистр.Имя"); без левой части), так как они бессмысленно расходуют ресурсы памяти и загрязняют исходный код.
  2. Если даты соглашения могут лежать за пределами расчетного интервала отчета по суммам, всегда разделяйте сбор финансовых оборотов через запрос к регистру и получение нормативно-справочных сведений через объектную выборку или периодические реквизиты.
← На главную