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