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

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

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

Анализ проблемы: почему данные в запросе могут быть неверными

Когда мы проводим документ, например, РасходнаяНакладная, и пытаемся в этот момент вычислить себестоимость, мы обращаемся к виртуальным таблицам остатков или оборотов. Здесь кроются две основные ловушки для начинающего разработчика:

  1. Влияние текущего документа: Если документ уже успел записать свои движения в регистр перед выполнением запроса, то виртуальная таблица Остатки вернет данные уже с учетом этого списания. Это приведет к занижению количества и искажению цены.
  2. Точка во времени: Использование просто параметра &МоментВремени в запросе не всегда дает нужный результат, так как момент времени включает в себя и сам документ. Если документ перепроводится, важно «отсечь» его старые движения.

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

Решение 1: Использование объекта Граница для управления временем

Чтобы запрос игнорировал движения самого проводимого документа, необходимо использовать объект Граница. Рассмотрим подробнее, как это работает. Когда мы передаем в параметр виртуальной таблицы просто дату или момент времени, система может включить движения документа, происходящие в ту же секунду.

Для корректного получения остатков «до» совершения текущей операции, используем следующий код:


ГраницаКонтроля = Новый Граница(МоментВремени(), ВидГраницы.Исключая);
Запрос.УстановитьПараметр("МоментВремени", ГраницаКонтроля);

Применяя ВидГраницы.Исключая, мы приказываем системе: «Возьми все данные из регистра строго до этого момента, не включая движения самого документа, который сейчас проводится». Это позволяет получить чистые остатки на складе, которые были зафиксированы до начала продажи.

Решение 2: Переход от Остатков к Оборотам

В некоторых специфических задачах (как в примере автора) требуется посчитать себестоимость исходя из общего объема закупок, а не из текущего остатка на складе. Если мы используем таблицу Остатки, мы видим только то, что осталось после всех предыдущих продаж. Если же нам нужно знать, сколько всего товара пришло и по какой средней цене он закупался исторически, нам поможет виртуальная таблица Обороты.

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

Рассмотрим пример правильно составленного запроса, который собирает данные о стоимости из остатков, а о количестве — из оборотов прихода:


Запрос2 = Новый Запрос;
Запрос2.МенеджерВременныхТаблиц = МенеджерВТ;
Запрос2.Текст = "ВЫБРАТЬ
|   НоменклатураДокумента.Номенклатура КАК Номенклатура,
|   НоменклатураДокумента.КоличествоВДокументе КАК КоличествоВДокументе,
|   ЕСТЬNULL(СтоимостьМатериаловОстатки.СтоимостьОстаток, 0) КАК Стоимость,
|   ЕСТЬNULL(ОстаткиМатериаловОбороты.КоличествоПриход, 0) КАК Количество
|ИЗ
|   НоменклатураДокумента КАК НоменклатураДокумента
|   ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.СтоимостьМатериалов.Остатки(
|       &МоментВремени,
|       Товар В (ВЫБРАТЬ НоменклатураДокумента.Номенклатура ИЗ НоменклатураДокумента)
|   ) КАК СтоимостьМатериаловОстатки
|   ПО НоменклатураДокумента.Номенклатура = СтоимостьМатериаловОстатки.Товар
|   ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ОстаткиМатериалов.Обороты(
|       ,
|       &МоментВремени,
|       ,
|       Товар В (ВЫБРАТЬ НоменклатураДокумента.Номенклатура ИЗ НоменклатураДокумента)
|   ) КАК ОстаткиМатериаловОбороты
|   ПО НоменклатураДокумента.Номенклатура = ОстаткиМатериаловОбороты.Товар";

// Устанавливаем границу исключая текущий документ
Граница = Новый Граница(МоментВремени, ВидГраницы.Исключая);
Запрос2.УстановитьПараметр("МоментВремени", Граница);

В данном примере мы используем левое соединение временной таблицы с двумя виртуальными таблицами. Обратите внимание: ресурс КоличествоПриход из таблицы Обороты позволяет увидеть все поступления товара с начала ведения учета до момента текущего документа.

Важные настройки документа и регистров

Посмотрим на настройки самого объекта Документ в конфигураторе. Для того чтобы предложенный выше код работал стабильно, необходимо убедиться в следующих настройках:

Типичные ошибки при расчете себестоимости по средней

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

  1. Пустые значения: Всегда используйте функцию ЕСТЬNULL в запросе. Если прихода товара еще не было, система вернет NULL, что при делении вызовет ошибку или даст нулевой результат.
  2. Разрез Склада: В запросе к виртуальным таблицам обязательно передавайте фильтр по складу, если учет ведется в разрезе складов. В коде автора склад не был указан в параметрах виртуальной таблицы, что является грубой ошибкой: система будет считать среднюю цену по всем складам компании сразу.
  3. Вид номенклатуры: Если в табличной части есть и товары, и услуги, помните, что услуги не имеют себестоимости на складе и не хранятся в регистре ОстаткиМатериалов.

Таким образом, для корректного расчета себестоимости мы должны обеспечить чистоту данных (удаление старых движений), точность момента времени (использование Граница) и правильный выбор ресурсов (Остатки или Обороты в зависимости от методики учета, принятой в компании).

← На главную