В практике разработчика на платформе 1С:Предприятие 7.7 часто возникает задача получения себестоимости товаров непосредственно из документа «Реализация». Типичная ситуация: в табличной части документа множество позиций, а в настройках списания партий указан «Автоподбор». Если пытаться рассчитать себестоимость «на лету» путем прямого перебора справочников и регистров остатков, это может привести к катастрофическому падению производительности, особенно в крупных информационных базах.
В этой статье мы подробно разберем, почему стандартные методы расчета могут работать медленно, и рассмотрим самый оптимальный способ получения данных, который уже заложен в архитектуру платформы.
Давайте проанализируем ситуацию. Когда мы открываем проведенный документ реализации и пытаемся программно вычислить себестоимость каждой строки, обращаясь к регистру остатков на момент времени документа, мы фактически заставляем систему выполнять ту же тяжелую работу, которую она уже проделала при проведении этого документа. Алгоритм «Автоподбора» должен найти свободные остатки по методу FIFO или LIFO, распределить их по партиям и учесть специфику конкретного склада. Повторное выполнение этих действий для каждой строки табличной части в цикле — это избыточная нагрузка на сервер и дисковую подсистему.
Самый быстрый и правильный способ получить себестоимость для уже проведенного документа — это **чтение его собственных движений по регистру**. Выясним причину, почему это эффективно: в момент проведения документа система записывает итоговые суммы списания (себестоимость) в физические таблицы движений регистров (например, ПартииНаличие или ПартииТоваров). Эти данные уже рассчитаны, распределены по партиям и «привязаны» к уникальному идентификатору документа IDDOC.
Рассмотрим по шагам, как реализовать этот алгоритм программно:
Регистр, соответствующий вашему регистру партий.ВыбратьДвиженияДокумента(), чтобы ограничить выборку записями только этого конкретного документа.Стоимость).Посмотрим на пример реализации этого метода:
// Создаем объект регистра
РегПартии = СоздатьОбъект("Регистр.ПартииНаличие");
// Выбираем движения конкретного документа (ТекущийДокумент)
РегПартии.ВыбратьДвиженияДокумента(Контекст.ТекущийДокумент());
Пока РегПартии.ПолучитьДвижение() = 1 Цикл
// Нас интересуют только расходные движения (списание товара)
Если РегПартии.Расход = 1 Тогда
Товар = РегПартии.Номенклатура;
СуммаСебестоимости = РегПартии.Стоимость;
Количество = РегПартии.Количество;
// Здесь можно накапливать данные в Таблицу Значений
// или сопоставлять их со строками ТЧ документа
КонецЕсли;
КонецЦикла;
Часто возникает вопрос: как быть, если один и тот же товар встречается в документе несколько раз в разных строках? Если мы просто выберем движения по номенклатуре, мы получим общую сумму, которую будет сложно распределить обратно. Проанализируем ситуацию глубже: в типовых конфигурациях (например, «Торговля и Склад» 7.7) при проведении используется метод ПривязыватьСтроку().
Если в модуле проведения вашего документа была команда Регистр.ИмяРегистра.ПривязыватьСтроку(НомерСтроки), то при чтении движений вы можете обратиться к методу НомерСтроки() объекта Регистр. Это позволяет получить себестоимость с точностью до конкретной позиции в табличной части, что исключает любые ошибки округления или распределения.
Рассмотрим случай, когда документ еще не проведен (например, менеджер хочет увидеть предполагаемую прибыль в форме нового документа), и движений в регистре еще нет. В этом случае чтение движений не сработает. Нам поможет механизм временного расчета.
Вместо полного перебора партий вручную, мы можем заставить систему рассчитать итоги на нужную точку времени. Разберем этот механизм:
Рег = СоздатьОбъект("Регистр.ПартииНаличие");
// Устанавливаем фильтр по нужной номенклатуре
Рег.УстановитьФильтр(ВыбТовар);
// Включаем режим временного расчета
Рег.ВременныйРасчет(1);
// Рассчитываем остатки на момент времени перед документом
РассчитатьРегистрыНа(ТекущийДокумент());
// Теперь можем получать остатки и стоимость стандартными методами
ОстатокСтоимости = Рег.СводныйОстаток(ВыбТовар, , "Стоимость");
Важно: Метод ВременныйРасчет гораздо быстрее, чем ручной перебор справочников, так как платформа оптимизирует запросы к базе данных на уровне ядра.
В некоторых редакциях конфигураций на базе 1С 7.7 (например, модификации «Торговля и Склад») себестоимость при проведении дублируется в регистр Продажи. Проанализируем этот вариант. Регистр Продажи обычно является «оборотистым», в нем меньше измерений (нет разреза по партиям и складам), чем в регистре ПартииНаличие. Если ваша цель — просто получить итоговую цифру себестоимости для отчета, выборка из регистра Продажи может отработать еще быстрее за счет меньшего объема данных в таблице.
Если вы работаете в среде SQL и используете внешние компоненты, такие как 1С++, самым эффективным способом будет выполнение прямого SQL-запроса к таблице движений регистра. Идентификатор таблицы движений обычно имеет вид RAxxxx (где xxxx — внутренний ID регистра). Прямой запрос к SQL-серверу с фильтром по полю IDDOC выполняется практически мгновенно, так как база данных использует заранее построенные индексы по документам.
Подводя итог, можно выделить следующие правила для получения оптимальной производительности в 1С 7.7:
ВыбратьДвиженияДокумента(). Это самый быстрый путь, не требующий лишних вычислений.ВременныйРасчет(1) и РассчитатьРегистрыНа().ПривязыватьСтроку(). Если нет — добавьте эту возможность, чтобы иметь точную связь движений со строками документа.Соблюдение этих принципов позволит вам создавать быстрые и надежные механизмы анализа себестоимости даже в очень нагруженных системах на базе 1С 7.7.