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

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

В процессе разработки или поддержки самописных конфигураций на платформе 1С 7.7 часто возникает проблема производительности, связанная с обработкой больших объемов данных в табличных частях документов. Типичная ситуация: документ «График оплаты» содержит десятки строк (мест аренды) и 31 колонку (дни месяца), где отметки об оплате или задолженности ставятся вручную. Когда количество договоров растет, обычный перебор документов и строк начинает занимать значительное время — минуты вместо секунд. Разберем пошагово, как можно оптимизировать этот процесс, начиная от простого улучшения кода и заканчивая изменением архитектуры данных.

Метод 1. Линеаризация и оптимизация стандартного кода 1С

Зачастую основной «тормоз» в коде — это глубокая вложенность условий Если...КонецЕсли и избыточные проверки внутри циклов. Проанализируем исходную ситуацию: программа для каждого договора заново открывает выборку документов и начинает вложенный цикл по строкам и колонкам. Рассмотрим, как можно ускорить выполнение, используя операторы Продолжить и предварительный расчет условий.

Вместо того чтобы строить «лестницу» из условий, мы можем отсекать неподходящие документы сразу. Посмотрим на оптимизированный пример функции ПроверитьДоговор:


Функция ПроверитьДоговор(Договор)
    ДолгиЗа = "";
    Оплата = СоздатьОбъект("Документ.ГрафикОплаты");
    // Ограничиваем выборку периодом действия договора
    Оплата.ВыбратьДокументы(НачМесяца(Договор.СрокС), КонМесяца(Договор.СрокПо));
    
    Пока Оплата.ПолучитьДокумент() = 1 Цикл
        Оплата_ПериодПо = КонМесяца(Оплата.Период);
        
        // Быстрые проверки на несоответствие условиям (отсекаем лишнее)
        Если Оплата.Сектор <> Договор.Место.Родитель Тогда Продолжить; КонецЕсли;
        Если Договор.СрокС > Оплата_ПериодПо Тогда Продолжить; КонецЕсли;
        Если Договор.СрокПо < Оплата.Период Тогда Продолжить; КонецЕсли;
        
        Оплата.ВыбратьСтроки();
        Пока Оплата.ПолучитьСтроку() = 1 Цикл
            Если Оплата.Место <> Договор.Место Тогда Продолжить; КонецЕсли;
            
            // Вычисляем диапазон дней один раз для строки
            НачЧисло = ДатаЧисло(?(Оплата.Период < Договор.СрокС, Договор.СрокС, Оплата.Период));
            КонЧисло = ДатаЧисло(?(Оплата_ПериодПо > Договор.СрокПо, Договор.СрокПо, Оплата_ПериодПо));
            
            // Проверка задолженности по колонкам Д1...Д31
            Для День = НачЧисло По КонЧисло Цикл
                Пометка = Оплата.ПолучитьАтрибут("Д" + Строка(День));
                Если Пометка = "Н" Тогда // Нашли метку неоплаты
                    ДолгиЗа = ДолгиЗа + Формат(Оплата.Период, "ДММММГГГГ") + " ";
                    Прервать; // Выходим из цикла по дням, так как долг найден
                КонецЕсли;
            КонецЦикла;
            
            Прервать; // Нашли нужное место, другие строки документа не интересуют
        КонецЦикла;
    КонецЦикла;
    Возврат(ДолгиЗа);
КонецФункции

Важный момент: использование метода ПолучитьАтрибут в цикле по 31 колонке работает медленнее, чем прямое обращение, но так как идентификаторы колонок динамические (Д1, Д2...), это неизбежно в рамках объектной модели. Однако вынос расчета НачЧисло и КонЧисло за пределы цикла Для и использование Прервать существенно сокращают количество операций.

Метод 2. Использование черных запросов с динамическим текстом

Стандартный запрос 1С (так называемый «черный запрос») может работать быстрее, чем ВыбратьДокументы, так как он оптимизирован на уровне движка базы данных. Однако основной проблемой является перебор колонок Д1...Д31. Мы можем сформировать текст запроса динамически, чтобы система сама фильтровала строки, где есть хотя бы одна отметка «Н».

Проанализируем ситуацию: нам нужно проверить все 31 колонку. Мы можем программно собрать условие запроса:


ТекстЗапроса = "
|Период с ДатаНач по ДатаКон;
|Док = Документ.ГрафикОплаты.ТекущийДокумент;
|Место = Документ.ГрафикОплаты.Место;";

Для ы = 1 По 31 Цикл
    ТекстЗапроса = ТекстЗапроса + "
    |Д" + ы + " = Документ.ГрафикОплаты.Д" + ы + ";";
КонецЦикла;

// Добавляем условие: хотя бы в одном дне должна быть буква "Н"
УсловиеСтр = "Условие((";
Для ы = 1 По 31 Цикл
    УсловиеСтр = УсловиеСтр + "Д" + ы + " = ""Н"")" + ?(ы = 31, "", " ИЛИ (");
КонецЦикла;
ТекстЗапроса = ТекстЗапроса + УсловиеСтр + ");";

Такой запрос вернет только те строки документов, которые действительно содержат задолженность. Это избавит нас от необходимости программного перебора «пустых» (оплаченных) строк в коде 1С.

Метод 3. Архитектурное решение: Регистры остатков

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

Разберем алгоритм работы с регистром:

  1. Создаем регистр остатков ЗадолженностьПоАренде.
  2. Измерения: Договор, Место, ДатаДолга (тип Дата).
  3. Ресурс: Сумма (или просто флаг 1, если важен сам факт).
  4. При проведении документа «Договор» или начислении аренды делаем движение Приход по регистру на каждый день месяца.
  5. В документе «График оплаты» при установке галочки (или снятии буквы «Н») при проведении делаем движение Расход.

В этом случае получение задолженности сводится к одной строке кода Регистр.ЗадолженностьПоАренде.Остаток(...). Это работает на порядок быстрее любых запросов к документам, так как система обращается к предварительно рассчитанной таблице итогов.

Метод 4. Использование внешних компонент (1CPP и прямые запросы)

Для опытных разработчиков 1С 7.7 существует «тяжелая артиллерия» — внешняя компонента 1CPP.dll. Она позволяет выполнять прямые SQL-запросы к базе данных, минуя объектную среду 1С.

Разберем преимущества этого подхода:

Пример использования 1CPP для поиска данных в табличной части (упрощенно):


ТекстЗапроса = "
|SELECT 
|    Док.IDDOC as [Документ $Документ.ГрафикОплаты],
|    ТЧ.sp123 as [Место $Справочник.Места] -- предположим, что это ID реквизита Место
|FROM 
|    dt123 as ТЧ -- таблица табличной части
|INNER JOIN 
|    _1sjourn as Док ON ТЧ.IDDOC = Док.IDDOC
|WHERE 
|    ТЧ.sp124 = 'Н' -- поиск буквы Н в конкретной колонке
|";

Метод 5. Оптимизация интерфейса и периодические реквизиты

Если пользователям важно быстро отмечать оплату «мышкой», и это является причиной отказа от классической схемы проведения документов, можно применить комбинированный подход:

Рассмотрим использование периодических реквизитов справочника. В справочнике Договоры можно завести периодический реквизит СтатусОплаты. При записи или проведении документа «График оплаты» можно программно устанавливать значение этого реквизита на конкретные даты. Преимущество в том, что метод ВыбратьЗначения() объекта Периодический работает очень быстро и позволяет построить отчет о долгах, не обращаясь к документам вообще.

Подведем итоги: для ускорения работы в 1С 7.7 в первую очередь стоит оптимизировать циклы (убрать вложенность), во вторую — рассмотреть возможность использования регистров, а для максимальной производительности использовать прямые запросы через 1CPP. Это позволит сократить время формирования отчетов с минут до нескольких секунд.

← На главную