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