Как правильно рассчитать и записать себестоимость при формировании движений документа в 1С?

Программист 1С v8.3 (Управляемые формы) Управленческий учет Торговля и дистрибуция
← На главную

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

Основные этапы решения задачи

Для решения поставленной задачи нам необходимо выполнить несколько шагов: сформировать запрос к регистру накопления ОстаткиНаСкладах, извлечь данные об остатках, вычислить себестоимость и передать её в движения документа. При этом важно корректно обработать ситуацию, когда остатки по количеству равны нулю, чтобы предотвратить аварийное завершение программы.

Исправление ошибки деления на ноль

Самая распространенная проблема при расчете средней себестоимости — это ошибка Деление на 0 значения типа Numeric. Она возникает, когда в базе данных числится сумма по товару (например, из-за накопленных погрешностей округления), а количество товара на складе равно нулю. Если мы просто разделим СуммаОстаток на КоличествоОстаток, система выдаст критическую ошибку.

Чтобы этого избежать, мы используем конструкцию ВЫБОР КОГДА непосредственно в тексте запроса. Это позволяет нам проверить значение делителя перед выполнением математической операции.

Посмотрите, как должен выглядеть исправленный код запроса:


Функция Себестоимость(МойТовар)
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ
        |   ВЫБОР 
        |       КОГДА ОстаткиНаСкладахОстатки.КоличествоОстаток <> 0 
        |       ТОГДА ОстаткиНаСкладахОстатки.СуммаОстаток / ОстаткиНаСкладахОстатки.КоличествоОстаток 
        |       ИНАЧЕ 0 
        |   КОНЕЦ КАК Себестоимость
        |ИЗ
        |   РегистрНакопления.ОстаткиНаСкладах.Остатки(, 
        |       Организация = &Организация 
        |       И Номенклатура = &МойТовар) КАК ОстаткиНаСкладахОстатки";
        
    Запрос.УстановитьПараметр("Организация", Организация);
    Запрос.УстановитьПараметр("МойТовар", МойТовар);
    
    РезультатЗапроса = Запрос.Выполнить();
    Выборка = РезультатЗапроса.Выбрать();
    
    Себестоимость = 0;
    Если Выборка.Следующий() Тогда
        Себестоимость = Выборка.Себестоимость;
    КонецЕсли;
    
    Возврат Себестоимость;
КонецФункции

Интеграция функции в процедуру проведения

После того как функция расчета себестоимости готова, мы можем вызывать её при формировании движений в цикле. Мы будем умножать количество товара в строке табличной части на полученную себестоимость:


Для Каждого ТекСтрокаТовары Из Товары Цикл
    Движение = Движения.ОстаткиНаСкладах.Добавить();
    // ... заполнение прочих реквизитов ...
    
    СуммаСебестоимости = Себестоимость(ТекСтрокаТовары.Номенклатура);
    Движение.Сумма = ТекСтрокаТовары.Количество * СуммаСебестоимости;
КонецЦикла;

Важные рекомендации по оптимизации

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

  1. Проблема запросов в цикле: Выполнение запроса внутри цикла Для Каждого — это тяжелая операция. Если в документе много строк, система будет обращаться к базе данных многократно, что приведет к существенному замедлению работы (так называемые «тормоза»).
  2. Правильный подход к оптимизации: В профессиональной разработке принято собирать все номенклатуры из табличной части в массив или список значений, а затем выполнять один запрос ко всей номенклатуре сразу. Результат такого запроса лучше поместить в Соответствие, из которого потом брать данные при обходе строк документа.
  3. Актуальность данных: Помните, что данные в регистре накопления могут меняться в процессе проведения, поэтому расчет средней стоимости «на лету» требует осторожности и понимания логики работы системы учета, принятой в вашей конфигурации.

Применяя данные рекомендации, вы не только решите задачу, но и обеспечите высокую скорость и надежность работы вашего кода в 1С!

← На главную