Как рассчитать процент использования материалов в 1С 7.7, отсортировать по нему отчет и ускорить его работу?

Программист 1С v7.7 1C:Бухгалтерия Бухгалтерский учет
← На главную

В legacy-системах на платформе 1С:Предприятие 7.7 разработчики часто сталкиваются с необходимостью доработки типовых или самописных отчетов, построенных на механизме бухгалтерских итогов. Типичная задача — рассчитать процент использования материальных запасов за период, вывести этот показатель в печатную форму и предоставить пользователю возможность сортировки по нему.

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

Часть 1. Выясняем, где рассчитывается процент использования материала

Проанализируем исходный код процедуры, предоставленной автором темы. Логика отбора материалов, у которых процент использования за период составил менее 30% от остатка, скрывается в следующей строке кода:

Если (Ит.КО(3)) < ((Ит.СКД(3) - Ит.СКК(3)) * 0.3) Тогда

Рассмотрим детально, что означают эти функции объекта БухгалтерскиеИтоги:

Таким образом, условие отбирает те материалы, расход которых (кредитовый оборот) оказался строго меньше, чем 30% от их конечного остатка. Если мы хотим вывести точный процент использования материала в отдельную колонку отчета, нам потребуется следующая математическая формула:

Процент использования = (Расход за период / Конечный остаток) * 100%

В коде 1С 7.7 расчет этой величины с защитой от деления на ноль будет выглядеть так:


КонечныйОстаток = Ит.СКД(3) - Ит.СКК(3);
Расход = Ит.КО(3);

Если КонечныйОстаток = 0 Тогда
    ПроцентИспользования = 0;
Иначе
    ПроцентИспользования = Окр((Расход / КонечныйОстаток) * 100, 2);
КонецЕсли;

Часть 2. Решаем проблему сортировки данных в 1С 7.7

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

Единственный надежный и правильный способ решить эту задачу — применить промежуточную буферизацию данных в объект ТаблицаЗначений (ТЗ). Рассмотрим этот алгоритм по шагам:

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

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


// Создаем таблицу значений для накопления данных перед выводом
ТЗ = СоздатьОбъект("ТаблицаЗначений");
ТЗ.НоваяКолонка("Склад", "Справочник.МестаХранения");
ТЗ.НоваяКолонка("МОЛ", "Справочник.Сотрудники");
ТЗ.НоваяКолонка("Материал", "Справочник.Материалы");
ТЗ.НоваяКолонка("НачальныйОстаток", "Число", 15, 3);
ТЗ.НоваяКолонка("Приход", "Число", 15, 3);
ТЗ.НоваяКолонка("Расход", "Число", 15, 3);
ТЗ.НоваяКолонка("КонечныйОстаток", "Число", 15, 3);
ТЗ.НоваяКолонка("Процент", "Число", 6, 2);

Теперь наполним созданную таблицу данных в цикле обхода выборки бухгалтерских итогов:


Ит.ВыбратьСубконто(ВидыСубконто.МестаХранения);
Пока Ит.ПолучитьСубконто(ВидыСубконто.МестаХранения) = 1 Цикл
    
    ТекущийСклад = Ит.Субконто(ВидыСубконто.МестаХранения);
    Ит.ВыбратьСубконто(ВидыСубконто.Сотрудники);
    
    Пока Ит.ПолучитьСубконто(ВидыСубконто.Сотрудники) = 1 Цикл
        
        ТекущийМОЛ = Ит.Субконто(ВидыСубконто.Сотрудники);
        Ит.ВыбратьСубконто(ВидыСубконто.Материалы);
        
        Пока Ит.ПолучитьСубконто(ВидыСубконто.Материалы) = 1 Цикл
            
            ТекущийМатериал = Ит.Субконто(ВидыСубконто.Материалы);
            
            // Исключаем группы материалов из детальных строк
            Если ТекущийМатериал.ЭтоГруппа() = 1 Тогда
                Продолжить;
            КонецЕсли;
            
            // Получаем количественные показатели
            НачОст = Ит.СНД(3) - Ит.СНК(3);
            Прих  = Ит.ДО(3);
            Раскх = Ит.КО(3);
            КонОст = Ит.СКД(3) - Ит.СКК(3);
            
            // Вычисляем процент использования
            Если КонОст = 0 Тогда
                Процент = 0;
            Иначе
                Процент = Окр((Раскх / КонОст) * 100, 2);
            КонецЕсли;
            
            // Добавляем запись в таблицу значений
            ТЗ.НоваяСтрока();
            ТЗ.Склад = ТекущийСклад;
            ТЗ.МОЛ = ТекущийМОЛ;
            ТЗ.Материал = ТекущийМатериал;
            ТЗ.НачальныйОстаток = НачОст;
            ТЗ.Приход = Прих;
            ТЗ.Расход = Раскх;
            ТЗ.КонечныйОстаток = КонОст;
            ТЗ.Процент = Процент;
            
        КонецЦикла;
    КонецЦикла;
КонецЦикла;

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


// Сортируем таблицу по убыванию процента использования
ТЗ.Сортировать("Процент-");

Теперь нам остается только вывести накопленные и отсортированные данные в макет отчета:


Таб = СоздатьОбъект("Таблица");
Таб.ИсходнаяТаблица("Сформировать");
Таб.ВывестиСекцию("Заголовок");
Таб.ВывестиСекцию("Шапка");

нпп = 0;
ТЗ.ВыбратьСтроки();
Пока ТЗ.ПолучитьСтроку() = 1 Цикл
    нпп = нпп + 1;
    
    // Передаем значения из ТЗ в переменные макета
    ВыбСклад = ТЗ.Склад;
    ВыбМОЛ = ТЗ.МОЛ;
    ВыбМатериал = ТЗ.Материал;
    НачальныйОстаток = ТЗ.НачальныйОстаток;
    Приход = ТЗ.Приход;
    Расход = ТЗ.Расход;
    КонечныйОстаток = ТЗ.КонечныйОстаток;
    ПроцентИспользования = ТЗ.Процент;
    
    // Выводим строку в печатную форму
    Таб.ВывестиСекцию("Строка");
КонецЦикла;

Таб.ВывестиСекцию("Подвал");
Таб.ТолькоПросмотр(1);
Таб.Показать("Отчет по использованию материалов");

Такой подход полностью решает задачу сортировки и гарантирует корректный вывод данных пользователю.

Часть 3. Оптимизация производительности: убираем «тормоза» отчета

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

Проблема кроется в конструкции вложенных циклов выборки субконто:


Пока Ит.ПолучитьСубконто(ВидыСубконто.МестаХранения) = 1 Цикл
    Пока Ит.ПолучитьСубконто(ВидыСубконто.Сотрудники) = 1 Цикл
        Пока Ит.ПолучитьСубконто(ВидыСубконто.Материалы) = 1 Цикл

Каждый раз при выполнении метода ПолучитьСубконто() на внутренних уровнях вложенности платформа 1С 7.7 совершает одиночные SQL-запросы (или обращения к DBF-файлам таблиц итогов) к базе данных. Если у вас в базе 10 складов, 50 материально ответственных лиц (МОЛ) и 1000 наименований материалов, общее количество потенциальных обращений к базе данных составит 10 * 50 * 1000 = 500 000 микрозапросов. Это неизбежно парализует работу системы.

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

Модернизируем наш алгоритм:


// Создаем объект БухгалтерскиеИтоги и выполняем запрос
Ит = СоздатьОбъект("БухгалтерскиеИтоги");
Ит.ИспользоватьСубконто(ВидыСубконто.МестаХранения,, 1);
Ит.ИспользоватьСубконто(ВидыСубконто.Сотрудники,, 1);
Ит.ИспользоватьСубконто(ВидыСубконто.Материалы, сзМатериалы, 1);

Если Ит.ВыполнитьЗапрос(выбНачПериода, ВыбКонПериода, списокСчетов) = 0 Тогда
    Предупреждение("Не удалось рассчитать бухгалтерские итоги!");
    Возврат;
КонецЕсли;

// Создаем пустую ТЗ и выгружаем в нее все итоги одним методом!
ТЗ_Итоги = СоздатьОбъект("ТаблицаЗначений");
// Параметры метода: ВыгрузитьТаблицу(ТЗ, ФлагГруппировок, ФлагПоказателей)
// ФлагГруппировок = 1 (выгружать детальные строки субконто)
// ФлагПоказателей = 1 (выгружать количественные и суммовые показатели)
Ит.ВыгрузитьТаблицу(ТЗ_Итоги, 1, 1);

Метод ВыгрузитьТаблицу() автоматически создаст ТЗ со структурой колонок, соответствующей субконто и показателям. Имена колонок для субконто будут иметь вид Субконто1, Субконто2, Субконто3, а показатели — СНД3 (Сальдо Начальное Дебет Количество), КО3 (Кредитовый оборот количество) и т.д.

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


// Добавим в выгруженную ТЗ колонку для расчета процента
ТЗ_Итоги.НоваяКолонка("Процент", "Число", 6, 2);

// Переименуем колонки для удобства обращения (необязательно, но полезно)
ТЗ_Итоги.УстановитьПараметрыКолонки("Субконто1", , , , "Склад");
ТЗ_Итоги.УстановитьПараметрыКолонки("Субконто2", , , , "МОЛ");
ТЗ_Итоги.УстановитьПараметрыКолонки("Субконто3", , , , "Материал");

// Рассчитываем проценты прямо в строках таблицы значений
ТЗ_Итоги.ВыбратьСтроки();
Пока ТЗ_Итоги.ПолучитьСтроку() = 1 Цикл
    
    // Исключаем строки-группировки, если они выгрузились
    Если ПустоеЗначение(ТЗ_Итоги.Материал) = 1 Тогда
        Продолжить;
    КонецЕсли;
    
    Если ТЗ_Итоги.Материал.ЭтоГруппа() = 1 Тогда
        Продолжить;
    КонецЕсли;
    
    // Вычисляем остаток: Дебетовое Сальдо Конечное - Кредитовое Сальдо Конечное (количественные колонки СКД3 и СКК3)
    КонОст = ТЗ_Итоги.СКД3 - ТЗ_Итоги.СКК3;
    Расход = ТЗ_Итоги.КО3; // Кредитовый оборот (количество)
    
    Если КонОст <= 0 Тогда
        ТЗ_Итоги.Процент = 0;
    Иначе
        ТЗ_Итоги.Процент = Окр((Расход / КонОст) * 100, 2);
    КонецЕсли;
    
КонецЦикла;

// Фильтруем ТЗ, оставляя только нужные строки (например, где процент < 30)
// Или просто сортируем для вывода
ТЗ_Итоги.Сортировать("Процент-");

Использование метода ВыгрузитьТаблицу() вместо вложенных циклов выборки субконто сокращает время формирования отчета на крупных базах данных с нескольких десятков минут до нескольких секунд. Это связано с тем, что вся тяжелая аналитическая работа по объединению таблиц перекладывается на плечи СУБД (SQL Server) или оптимизированного внутреннего движка 1С DBF, а последующая обработка данных происходит в оперативной памяти компьютера без повторных обращений к диску.

Резюме

Мы успешно решили задачу доработки отчета по использованию материалов в 1С 7.7. В ходе совместной работы мы:

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