Как реализовать списание себестоимости по средней цене при проведении накладной в 1С 8.3?

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

При разработке учетных систем на платформе 1С 8.3 одной из базовых задач является списание себестоимости товарно-материальных ценностей при их реализации. Начинающие разработчики часто пытаются рассчитывать себестоимость напрямую из документов поступления или привязывать суммы продаж к строкам расхода. Разберем правильный архитектурный подход к реализации списания по средней стоимости в модуле проведения документа.

Среднескользящая и средневзвешенная стоимость: принципиальная разница

Прежде чем приступать к кодированию, проанализируем различия двух методов расчета средней стоимости:

  1. Средневзвешенная стоимость. Рассчитывается за весь отчетный период (как правило, за месяц) как частное от деления совокупной стоимости остатка на начало и всех поступлений на их общее количество. Такой расчет не выполняется в момент проведения каждой накладной; списание себестоимости корректируется в конце месяца регламентным документом (например, «Закрытие месяца» или «Корректировка стоимости списания»).
  2. Среднескользящая стоимость. Рассчитывается непосредственно в момент проведения каждой расходной накладной. Мы определяем остаток количества и суммы партии на складе строго до момента текущей продажи и пропорционально списываем себестоимость. Именно этот вариант требуется для оперативного списания в онлайн-режиме.

Откуда брать остатки для расчета себестоимости

Типичная ошибка начинающего специалиста — попытка найти документ ПриходнаяНакладная и взять цену закупки непосредственно из его табличной части. В реальном учете товары могут поступать десятками документов по разным ценам, частично списываться и возвращаться поставщикам. Единственным достоверным источником актуального наличия и стоимости являются регистры — регистр накопления остатков или регистр бухгалтерии.

Для корректного списания себестоимости нам необходимо получить дебетовый остаток по счету учета номенклатуры (например, 41 «Товары» или субсчету «Товары на складах») в количественном и суммовом выражении на момент проведения документа.

Где и как описывать алгоритм

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

Разберем типовой пошаговый алгоритм проведения расходного документа:

  1. Подготовка списка номенклатуры. Выгружаем табличную часть документа и сворачиваем ее по номенклатуре (и складу, если ведется складской учет), суммируя количество. Если в накладной одна и та же позиция указана двумя строками, свертка предотвратит повторное обращение к уже списанному объему.
  2. Запрос к виртуальной таблице остатков. Обращаемся к таблице Остатки регистра бухгалтерии или регистра накопления, передавая в качестве границы времени МоментВремени() проводимого документа.
  3. Контроль остатка. Проверяем, хватает ли товара на складе для покрытия расхода. Если остатка недостаточно, формируем информативное сообщение пользователю и устанавливаем флаг Отказ = Истина.
  4. Расчет суммы и учет проблемы округления («копеек»). Если количество к списанию равно остатку на складе, мы обязаны списать всю оставшуюся сумму под ноль, не применяя формулу деления. Если же происходит частичное списание, вычисляем себестоимость по формуле: ОстатокСуммы / ОстатокКоличества * КоличествоСписания.
  5. Запись проводок. Заполняем движения по регистрам.

Пример программной реализации в процедуре ОбработкаПроведения

Посмотрим на практический пример кода для документа «Расходная накладная», списывающего товар с регистра бухгалтерии (проводка Дт 90.02 «Себестоимость» — Кт 41.01 «Товары на складах»):


Процедура ОбработкаПроведения(Отказ, РежимПроведения)
    
    // Очищаем старые движения и включаем запись
    Движения.ОсновнойРегистрБухгалтерии.Записывать = Истина;
    Движения.ОсновнойРегистрБухгалтерии.Очистить();
    
    // Подготавливаем таблицу товаров для анализа остатков
    ТаблицаТовары = Товары.Выгрузить();
    ТаблицаТовары.Свернуть("Номенклатура", "Количество, Сумма");
    
    // Формируем запрос к остаткам регистра бухгалтерии
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ
        |   ДокТовары.Номенклатура КАК Номенклатура,
        |   ДокТовары.Количество КАК КоличествоВДокументе,
        |   ЕСТЬNULL(Остатки.КоличествоОстатокДт, 0) КАК КоличествоОстаток,
        |   ЕСТЬNULL(Остатки.СуммаОстатокДт, 0) КАК СуммаОстаток
        |ИЗ
        |   &ТаблицаТовары КАК ДокТовары
        |   ЛЕВОЕ СОЕДИНЕНИЕ РегистрБухгалтерии.ОсновнойРегистрБухгалтерии.Остатки(
        |       &МоментВремени,
        |       Счет = &СчетУчета,
        |       ,
        |       Субконто1 В (&СписокНоменклатуры) И Субконто2 = &Склад) КАК Остатки
        |   ПО ДокТовары.Номенклатура = Остатки.Субконто1";
        
    Запрос.УстановитьПараметр("ТаблицаТовары", ТаблицаТовары);
    Запрос.УстановитьПараметр("СписокНоменклатуры", ТаблицаТовары.ВыгрузитьКолонку("Номенклатура"));
    Запрос.УстановитьПараметр("СчетУчета", ПланыСчетов.Основной.ТоварыНаСкладах);
    Запрос.УстановитьПараметр("Склад", Склад);
    Запрос.УстановитьПараметр("МоментВремени", МоментВремени());
    
    Выборка = Запрос.Выполнить().Выбрать();
    
    Пока Выборка.Следующий() Цикл
        
        // Проверяем наличие требуемого количества
        Если Выборка.КоличествоОстаток < Выборка.КоличествоВДокументе Тогда
            Сообщение = Новый СообщениеПользователю;
            Сообщение.Текст = СтрШаблон("Недостаточно товара %1! В наличии: %2, требуется: %3", 
                Выборка.Номенклатура, Выборка.КоличествоОстаток, Выборка.КоличествоВДокументе);
            Сообщение.Сообщить();
            Отказ = Истина;
        КонецЕсли;
        
        Если Отказ Тогда
            Продолжить;
        КонецЕсли;
        
        // Расчет себестоимости с защитой от зависания копеек
        Если Выборка.КоличествоВДокументе = Выборка.КоличествоОстаток Тогда
            СебестоимостьСписания = Выборка.СуммаОстаток;
        Иначе
            СебестоимостьСписания = Окр((Выборка.СуммаОстаток / Выборка.КоличествоОстаток) * Выборка.КоличествоВДокументе, 2);
        КонецЕсли;
        
        // Формирование проводки списания себестоимости
        Движение = Движения.ОсновнойРегистрБухгалтерии.Добавить();
        Движение.Период = Дата;
        Движение.СчетДт = ПланыСчетов.Основной.Себестоимость;
        Движение.СчетКт = ПланыСчетов.Основной.ТоварыНаСкладах;
        Движение.Сумма = СебестоимостьСписания;
        Движение.КоличествоКт = Выборка.КоличествоВДокументе;
        Движение.СубконтоКт[ПланыВидовХарактеристик.ВидыСубконто.Номенклатура] = Выборка.Номенклатура;
        Движение.СубконтоКт[ПланыВидовХарактеристик.ВидыСубконто.Склады] = Склад;
        
    КонецЦикла;
    
КонецПроцедуры

Почему возникает ошибка нехватки товара, если приход оформлен?

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

  1. Некорректная временная последовательность документов. Если приход и расход введены одной датой, проверьте их точное время. Расходная накладная, созданная на 12:00:00, не увидит движений приходной, записанной на 15:00:00 или на конец дня. Для корректного учета приход должен быть строго раньше реализации по временной шкале.
  2. Использование Даты вместо МоментаВремени. Передавая в параметр виртуальной таблицы остатков обычную Дата, платформа обращается к остаткам на начало секунды без учета позиции самого документа в последовательности. Необходимо передавать системный объект МоментВремени().
  3. Неочищенные движения при перепроведении. Если документ перепроводят, а его предыдущие движения по списанию не были стерты, они уменьшают доступный остаток при расчете. Всегда устанавливайте свойство УдалениеДвижений в значение УдалятьАвтоматически либо очищайте наборы записей перед выполнением запроса.
  4. Несовпадение субконто или реквизитов шапки. Обратите внимание на совпадение склада, организации и отсутствие дублирующихся позиций в справочнике Номенклатура. Если товар оприходован на «Основной склад», а списывается с «Оптового», остаток найден не будет.
← На главную